SET CONSTRAINTS ALL DEFERRED 未按预期工作
SET CONSTRAINTS ALL DEFERRED not working as expected
在 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
个约束。
让我先推荐更好的选择:
1。 INSERT
顺序
反转 INSERT
语句的 顺序 ,无需延迟任何内容。最简单和最快的 - 如果可能的话。
2。单个命令
在单个命令中完成。然后仍然不需要延迟任何内容,因为在每个命令之后检查了不可延迟的约束并且 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<>fiddle here
相关:
- Constraint defined DEFERRABLE INITIALLY IMMEDIATE is still DEFERRED?
我原来的回答quoted the manual:
Referential actions other than the NO ACTION
check cannot be deferred,
even if the constraint is declared deferrable.
但这具有误导性,因为它仅适用于“引用操作”,即对引用的 table 中的行发生 ON UPDATE
或 ON DELETE
的情况。手头的案例不是其中之一 - 如 .
我同意其他人的看法,正确的方法是按正确的顺序进行 - 但有时这不是一个可行的选择,需要更简单的方法才能在时间预算内完成工作。
如果这对任何人有帮助,我制作了一个程序,可以自动将延迟选项添加到所有 FK,以便
SET CONSTRAINTS ALL DEFERRED;
命令将起作用。当然只在必要时使用它。
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
;
在 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
个约束。
让我先推荐更好的选择:
1。 INSERT
顺序
反转 INSERT
语句的 顺序 ,无需延迟任何内容。最简单和最快的 - 如果可能的话。
2。单个命令
在单个命令中完成。然后仍然不需要延迟任何内容,因为在每个命令之后检查了不可延迟的约束并且 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<>fiddle here
相关:
- Constraint defined DEFERRABLE INITIALLY IMMEDIATE is still DEFERRED?
我原来的回答quoted the manual:
Referential actions other than the
NO ACTION
check cannot be deferred, even if the constraint is declared deferrable.
但这具有误导性,因为它仅适用于“引用操作”,即对引用的 table 中的行发生 ON UPDATE
或 ON DELETE
的情况。手头的案例不是其中之一 - 如
我同意其他人的看法,正确的方法是按正确的顺序进行 - 但有时这不是一个可行的选择,需要更简单的方法才能在时间预算内完成工作。
如果这对任何人有帮助,我制作了一个程序,可以自动将延迟选项添加到所有 FK,以便
SET CONSTRAINTS ALL DEFERRED;
命令将起作用。当然只在必要时使用它。
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
;