web-dev-qa-db-ja.com

postgresqlの外部キーはトリガーによって違反される可能性があります

Postgresでいくつかのテーブルを作成し、テーブル間で外部キーを追加し、ON DELETEをCASCADEに設定しました。不思議なことに、この制約に違反しているように見えるフィールドがいくつかあります。

これは正常な動作ですか?もしそうなら、私が望む動作を得る方法はありますか(違反はあり得ません)?

編集:

私は元々CREATE TABLEの一部として外部キーを作成しましたが、

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

PgAdmin3が提供する現在のコードは

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

編集2:

明確にするために、更新/挿入が行われたときにのみ制約がチェックされるが、その後は決して見られないというこっそりの疑いがあります。残念ながら、これが真実であるかどうか、またはこれらのチェックを実行せずにフィールドがデータベース内でどのように終わるかを知るには、postgresについて十分に知りません。

これが事実である場合、すべての外部キーをチェックしてそれらの問題を修正する方法はありますか?

編集3:

制約違反は、誤ったトリガーによって引き起こされる可能性があります。以下を参照してください

21
Michael Clerx

これまでに読んだすべては、データが挿入されたときにのみ制約がチェックされることを示唆しているようです。 (または、制約が作成されたとき)たとえば、 制約の設定に関するマニュアル です。

これは理にかなっており、データベースが適切に機能していれば十分です。 これをどうやって回避できたのか、あるいは状況を間違って読んだだけで、そもそも実際の制約違反がなかったのか、私はまだ興味があります。

いずれにしても、ケースはクローズされました:-/

-------更新--------

不完全なトリガーによって引き起こされた制約違反は確かにありました。複製するスクリプトは次のとおりです。

-- 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;
5
Michael Clerx

外部キー制約が適用されていることを示す簡単な例を作成しようとしました。この例では、fkに違反するデータの入力が許可されていないことを証明し、fkが挿入中に所定の位置にない場合、fkを有効にすると、fk制約がエラーをスローし、データがfkに違反していることを通知します。そのため、適切なfkに違反するデータがテーブルにどのようにあるかはわかりません。私は9.0を使用していますが、これは8.3でも同じです。あなたが役立つかもしれないあなたの問題を証明する実用的な例を示すことができれば。

--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;
24
Kuberchaun