2010-10-18 14 views
20

Stworzyłem kilka tabel w postgresie, dodałem klucz obcy z jednej tabeli do drugiej i ustawiłem DELETE na CASCADE. O dziwo, mam pewne pola, które wydają się naruszać to ograniczenie.Klucze obce w postgresql mogą zostać naruszone przez wyzwalacz

Czy to normalne zachowanie? A jeśli tak, to czy istnieje sposób, aby uzyskać pożądane zachowanie (bez naruszeń)?

Edit:

I orginaly utworzony klucz obcy w ramach CREATE TABLE, tylko przy użyciu

... REFERENCES product (id) ON UPDATE CASCADE ON DELETE CASCADE 

Obecny kod pgAdmin3 daje się

ALTER TABLE cultivar 
    ADD CONSTRAINT cultivar_id_fkey FOREIGN KEY (id) 
     REFERENCES product (id) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE CASCADE; 

Edit 2:

Aby wyjaśnić, mam podejrzenie, że ograniczenia są sprawdzane tylko przy aktualizacji s/inserty zdarzają się, ale nigdy potem nie są ponownie oglądane. Niestety nie wiem wystarczająco dużo na temat PostgreSQL, aby dowiedzieć się, czy jest to prawdą, czy też w jaki sposób pola mogą znaleźć się w bazie danych bez przeprowadzania tych kontroli.

Jeśli tak, czy jest jakiś sposób sprawdzenia wszystkich kluczy obcych i naprawienia tych problemów?

Edit 3:

naruszenie więzów może być spowodowane przez wadliwy wyzwalacz, patrz poniżej

+0

tylko za sprawdzenie poprawności możesz wysłać oświadczenie użyte do utworzenia FK? –

+0

W jakiej wersji PG jesteś? – Kuberchaun

+0

Używam wersji 8.3 –

Odpowiedz

5

Wszystko czytałem do tej pory wydaje się sugerować, że ograniczenia są sprawdzane tylko wtedy, gdy dane jest włożona. (Lub gdy tworzone jest ograniczenie) Na przykład the manual on set constraints.

Ma to sens i - jeśli baza danych działa poprawnie - powinno być wystarczająco dobre. Wciąż jestem ciekawa, jak udało mi się to obejść lub gdybym po prostu źle odczytał sytuację i na początku nie było żadnego prawdziwego naruszenia ograniczeń.

Tak czy inaczej, sprawa zamknięta: -/

------- UPDATE --------

Było zdecydowanie do naruszenia ograniczenia, spowodowane wadliwym spuście. Oto skrypt do replikowania:

-- Create master table 
CREATE TABLE product 
(
    id INT NOT NULL PRIMARY KEY 
); 

-- Create second table, referencing the first 
CREATE TABLE example 
(
    id int PRIMARY KEY REFERENCES product (id) ON DELETE CASCADE 
); 

-- Create a (broken) trigger function 
--CREATE LANGUAGE plpgsql; 
CREATE OR REPLACE FUNCTION delete_product() 
    RETURNS trigger AS 
$BODY$ 
    BEGIN 
     DELETE FROM product WHERE product.id = OLD.id; 
     -- This is an error! 
     RETURN null; 
    END; 
$BODY$ 
    LANGUAGE plpgsql; 

-- Add it to the second table 
CREATE TRIGGER example_delete 
    BEFORE DELETE 
    ON example 
    FOR EACH ROW 
    EXECUTE PROCEDURE delete_product(); 

-- Now lets add a row 
INSERT INTO product (id) VALUES (1); 
INSERT INTO example (id) VALUES (1); 

-- And now lets delete the row 
DELETE FROM example WHERE id = 1; 

/* 
Now if everything is working, this should return two columns: 
(pid,eid)=(1,1). However, it returns only the example id, so 
(pid,eid)=(0,1). This means the foreign key constraint on the 
example table is violated. 
*/ 
SELECT product.id AS pid, example.id AS eid FROM product FULL JOIN example ON product.id = example.id; 
+1

Nigdy nie było prawdziwego naruszenia ograniczeń. – Unreason

+0

Zdałem sobie sprawę, że potraktowałem tę sytuację nieco dziwnie, więc zrobiłem dodatkowy wysiłek i powtórzyłem sytuację, która spowodowała moje początkowe problemy. –

24

Próbowałem utworzyć prosty przykład, który pokazuje egzekwowane ograniczenie klucza obcego. W tym przykładzie udowodniam, że nie wolno mi wprowadzać danych, które naruszają fk i udowadniam, że jeśli fk nie jest na miejscu podczas wstawiania, a ja włączam fk, to ograniczenie fk zgłasza błąd informujący, że dane naruszają fk. Więc nie widzę, jak masz dane w tabeli, które naruszają fk, który jest na miejscu. Jestem na 9.0, ale nie powinno to być inne na 8.3. Jeśli możesz pokazać przykład pracy, który udowodni, że Twój problem może pomóc.

--CREATE TABLES-- 
CREATE TABLE parent 
(
    parent_id integer NOT NULL, 
    first_name character varying(50) NOT NULL, 
    CONSTRAINT pk_parent PRIMARY KEY (parent_id) 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE parent OWNER TO postgres; 

CREATE TABLE child 
(
    child_id integer NOT NULL, 
    parent_id integer NOT NULL, 
    first_name character varying(50) NOT NULL, 
    CONSTRAINT pk_child PRIMARY KEY (child_id), 
    CONSTRAINT fk1_child FOREIGN KEY (parent_id) 
     REFERENCES parent (parent_id) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE CASCADE 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE child OWNER TO postgres; 
--CREATE TABLES-- 

--INSERT TEST DATA-- 
INSERT INTO parent(parent_id,first_name) 
SELECT 1,'Daddy' 
UNION 
SELECT 2,'Mommy'; 

INSERT INTO child(child_id,parent_id,first_name) 
SELECT 1,1,'Billy' 
UNION 
SELECT 2,1,'Jenny' 
UNION 
SELECT 3,1,'Kimmy' 
UNION 
SELECT 4,2,'Billy' 
UNION 
SELECT 5,2,'Jenny' 
UNION 
SELECT 6,2,'Kimmy'; 
--INSERT TEST DATA-- 

--SHOW THE DATA WE HAVE-- 
select parent.first_name, 
     child.first_name 
from parent 
inner join child 
     on child.parent_id = parent.parent_id 
order by parent.first_name, child.first_name asc; 
--SHOW THE DATA WE HAVE-- 

--DELETE PARENT WHO HAS CHILDREN-- 
BEGIN TRANSACTION; 
delete from parent 
where parent_id = 1; 

--Check to see if any children that were linked to Daddy are still there? 
--None there so the cascade delete worked. 
select parent.first_name, 
     child.first_name 
from parent 
right outer join child 
     on child.parent_id = parent.parent_id 
order by parent.first_name, child.first_name asc; 
ROLLBACK TRANSACTION; 


--TRY ALLOW NO REFERENTIAL DATA IN-- 
BEGIN TRANSACTION; 

--Get rid of fk constraint so we can insert red headed step child 
ALTER TABLE child DROP CONSTRAINT fk1_child; 

INSERT INTO child(child_id,parent_id,first_name) 
SELECT 7,99999,'Red Headed Step Child'; 

select parent.first_name, 
     child.first_name 
from parent 
right outer join child 
     on child.parent_id = parent.parent_id 
order by parent.first_name, child.first_name asc; 

--Will throw FK check violation because parent 99999 doesn't exist in parent table 
ALTER TABLE child 
    ADD CONSTRAINT fk1_child FOREIGN KEY (parent_id) 
     REFERENCES parent (parent_id) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE CASCADE; 

ROLLBACK TRANSACTION; 
--TRY ALLOW NO REFERENTIAL DATA IN-- 

--DROP TABLE parent; 
--DROP TABLE child; 
Powiązane problemy