2010-10-18 8 views
20

Ich habe einige Tabellen in Postgres erstellt, einen Fremdschlüssel aus einer Tabelle in einen anderen hinzugefügt und ON DELETE auf CASCADE gesetzt. Seltsamerweise habe ich einige Felder, die diese Einschränkung zu verletzen scheinen.Fremdschlüssel in Postgresql kann durch Auslöser verletzt werden

Ist das normales Verhalten? Und wenn ja, gibt es eine Möglichkeit, das gewünschte Verhalten zu erreichen (keine Verstöße möglich)?

Edit:

I orginaly der Fremdschlüssel als Teil erstellt Tabelle von CREATE, nur mit

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

Der aktuelle Code pgAdmin3

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

bearbeiten 2 ergibt:

Zur Klärung habe ich den schleichenden Verdacht, dass die Einschränkungen nur beim Update überprüft werden s/Inserts passieren aber werden dann nie wieder angeschaut. Leider weiß ich nicht genug über Postgres, um herauszufinden, ob das wahr ist oder wie Felder in der Datenbank landen könnten, ohne dass diese Prüfungen ausgeführt werden.

Wenn dies der Fall ist, gibt es eine Möglichkeit, alle Fremdschlüssel zu überprüfen und diese Probleme zu beheben?

bearbeiten 3:

eine Beschränkungsverletzung kann durch einen fehlerhaften Trigger ausgelöst werden, siehe unten

+0

nur für eine Gesundheitsprüfung können Sie die Anweisung, die Sie zum Erstellen der FK? –

+0

In welcher PG-Version sind Sie angemeldet? – Kuberchaun

+0

Ich benutze Version 8.3 –

Antwort

5

Alles, was ich bisher gelesen habe scheint darauf hinzudeuten, dass Beschränkungen nur überprüft, wenn die Daten eingefügt wird. (Oder wenn die Einschränkung erstellt wird) Zum Beispiel the manual on set constraints.

Das macht Sinn und sollte - wenn die Datenbank richtig funktioniert - gut genug sein. Ich bin immer noch neugierig, wie ich es geschafft habe, das zu umgehen, oder wenn ich die Situation einfach falsch gelesen habe und es zu Beginn nie eine echte Einschränkung gab.

So oder so, Fall geschlossen: -/

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

Es war auf jeden Fall eine Beschränkung verletzt, durch einen fehlerhaften Trigger verursacht. Hier ist ein Skript zu replizieren:

-- 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

Es gab nie eine echte Constraint-Verletzung. – Unreason

+0

Ich merkte, dass ich diese Situation ein wenig seltsam gehandhabt habe, also machte ich die zusätzlichen Anstrengungen und replizierte die Situation, die meine anfänglichen Probleme verursachte. –

24

Ich habe versucht, ein einfaches Beispiel zu erstellen, den Fremdschlüssel zeigt erzwungen werden. Mit diesem Beispiel beweise ich, dass ich keine Daten eingeben darf, die gegen fk verstoßen, und beweise, dass, wenn fk beim Einfügen nicht vorhanden ist und fk aktiviert wird, die fk-Einschränkung einen Fehler auslöst, der angibt, dass Daten gegen fk verstoßen. Ich sehe also nicht, wie Sie Daten in der Tabelle haben, die gegen ein vorhandenes fk verstoßen. Ich bin auf 9.0, aber das sollte nicht auf 8.3 anders sein. Wenn Sie ein funktionierendes Beispiel zeigen können, das Ihr Problem bestätigt, könnte dies hilfreich sein.

--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; 
Verwandte Themen