【问题标题】:SET CONSTRAINTS ALL DEFERRED not working as expectedSET CONSTRAINTS ALL DEFERRED 未按预期工作
【发布时间】:2015-04-25 04:36:15
【问题描述】:

在 PostgreSQL 9.3 数据库中,如果我定义表 ab 如下:

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

【问题讨论】:

标签: sql postgresql database-design foreign-keys referential-integrity


【解决方案1】:

只能推迟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小提琴here

相关:


我原来的回答quoted the manual

NO ACTION 检查之外的参考操作不能被推迟, 即使约束被声明为可延迟的。

但这具有误导性,因为它仅适用于“引用操作”,即 ON UPDATEON DELETE 对引用表中的行发生的情况。手头的案例不是其中之一 - 如@zer0hedge pointed out

【讨论】:

  • 如果外键约束不能永远受到SET CONSTRAINTS的影响,那么为什么SET CONSTRAINTS的文档状态:“目前,只有唯一的,主键, REFERENCES(外键),并且 EXCLUDE 约束受此设置影响”? postgresql.org/docs/current/static/sql-set-constraints.html
  • 没关系,想通了。它们可能会受到影响,但前提是您不使用 ON UPDATEON DELETE 操作,在这种情况下是 OP。
  • 我相信答案是错误的。在 ALTER TABLE 语句的末尾添加 DEFERRABLE 将解决此问题。
  • @zer0hedge:现在才看到这个。你是对的,我错了(关于第二部分)。相应地进行了修复,并添加了更多有用的信息。
【解决方案2】:

我同意其他人的观点,即正确的方法是按正确的顺序进行 - 但有时这不是一个可行的选择,需要更简单的方法才能在时间预算内完成工作。

如果这对任何人都有帮助,我制作了一个程序,可以自动将延迟选项添加到所有 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
;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-31
    • 2017-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-05
    相关资源
    最近更新 更多