PostgreSQL 9.3データベースで、テーブルa
とb
を次のように定義すると次のようになります。
CREATE TABLE a(i integer);
ALTER TABLE a ADD CONSTRAINT pkey_a PRIMARY KEY (i);
CREATE TABLE b(j integer);
ALTER TABLE b add CONSTRAINT fkey_ij FOREIGN KEY (j)
REFERENCES a (i) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE;
INSERT INTO a(i) VALUES(1);
そして、次のことを行います。
START TRANSACTION;
SET CONSTRAINTS ALL DEFERRED;
INSERT INTO b(j) VALUES(2);
INSERT INTO a(i) VALUES(2);
COMMIT;
以下のエラーが発生します。なぜSET CONSTRAINTS
望ましい効果がありませんか?
ERROR: insert or update on table "b" violates foreign key constraint "fkey_ij" SQL state: 23503 Detail: Key (j)=(2) is not present in table "a".
DEFERRABLE
制約のみを延期できます。
最初に優れた代替案を提案させてください。
INSERT
順番にINSERT
ステートメントのシーケンスを逆にすると、何も延期する必要はありません。最も単純で最速-可能であれば。
単一コマンドで実行します。次に、延期できない制約がチェックされるため、延期する必要はありません各コマンドの後におよびCTEは単一のコマンドの一部と見なされます。
WITH ins1 AS (
INSERT INTO b(j) VALUES(2)
)
INSERT INTO a(i) VALUES(2);
その間、最初のINSERT
の値を再利用できます。特定のケースまたは複数列のインサートに対してより安全/より便利:
WITH ins1 AS (
INSERT INTO b(j) VALUES(3)
RETURNING j
)
INSERT INTO a(i)
SELECT j FROM ins1;
ALTER TABLE b ADD CONSTRAINT fkey_ij FOREIGN KEY (j)
REFERENCES a (i) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE; -- !!!
その後、元のコードが機能します(制約が延期されるとコストが増えるため、少し遅くなります)。
db <>フィドル ここ
関連:
私の元の答え マニュアルを引用 :
制約が延期可能であると宣言されている場合でも、
NO ACTION
チェック以外の参照アクションは延期できません。
ただし、これは「参照アクション」、つまり参照テーブルの行にON UPDATE
またはON DELETE
が発生する場合にのみ適用されるため、誤解を招く恐れがありました。手元のケースはそれらの1つではありません @ zer0hedgeが指摘したように 。
私は他の人たちに同意します。それを行う正しい方法は正しい順序ですが、それが実行可能な選択肢ではなく、時間予算内で仕事を成し遂げるためにもっと簡単なことが必要な場合があります。
これが誰かに役立つ場合に備えて、すべてのFKに遅延オプションを追加することを自動化する手順を作成しました。
制約をすべて延期します。
コマンドは機能します。もちろん必要な場合にのみ使用してください。
DO
$$
DECLARE
temp_rec RECORD;
sql_exe TEXT;
BEGIN
sql_exe := $sql$
ALTER TABLE %1$s ALTER CONSTRAINT %2$s DEFERRABLE;
$sql$
;
FOR temp_rec IN
(select constraint_name, table_name from information_schema.table_constraints where constraint_type = 'FOREIGN KEY')
LOOP
EXECUTE format(sql_exe, temp_rec.table_name, temp_rec.constraint_name);
END LOOP;
END;
$$
LANGUAGE plpgsql
;