我运行以下 SQL,但从未提交事务:
rollback;
begin;
create table testing_stuff (
id serial,
num integer NOT NULL unique deferrable initially immediate
);
insert into testing_stuff (num) values (2), (1);
-- no issues with deferrable even though it swaps values
update testing_stuff set num = id;
-- fails even though I have not comitted;
update testing_stuff set num = 2;
-- this would have fixed it
update testing_stuff set num = id;
如果我删除
deferrable initially immediate
那么它会在 update testing_stuff set num = id;
上失败,因为每一行都会立即检查。所以 deferrable initially immediate
肯定是在推迟一些事情。
我的理解是,由于事务未提交,所以这里根本不应该检查任何约束,基于SET CONSTRAINTS文档,其中说:
DEFERRED 约束在事务提交之前不会被检查。
如果我删除线
update testing_stuff set num = 2;
,那么一切都会正常。
另外,如果我使用 deferrable initially deferred
那么我会得到我期望的延迟行为。
我对交易边界和/或
initially immediate
缺少什么? initially immediate
是否也适用于交易中的所有 future 语句?但为什么 deferrable initially immediate
会避免 update testing_stuff set num = id;
上的错误呢?
这里有两件事在起作用:
DEFERRABLE
不是DEFERRED
。 INITIALLY IMMEDIATE
部分意味着约束不是 在事务结束时检查,而是在每个语句之后检查。您需要编写 DEFERRABLE INITIALLY DEFERRED
让 PostgreSQL 在事务结束时检查约束。
DEFERRABLE
约束的实现方式与 PostgreSQL 中的 NOT DEFERRABLE
约束(默认)不同。
NOT DEFERRABLE
将行插入到唯一索引时会验证唯一约束,因此当交换两个值时,更改第一行会触发约束违规。
DEFERRABLE
唯一约束是通过在语句末尾执行的系统触发器来实现的,因此使用 DEFERRABLE
约束不会违反约束,因为在语句交换值后数据是一致的。
默认的
NOT DEFERRABLE
行为不符合SQL标准的定义,它要求在语句之后检查约束。 PostgreSQL 的默认行为是出于性能原因;如果您需要严格遵守标准,请使用 DEFERRABLE
约束。
如果违反约束,则会引发异常
deferrable initially deferred
deferrable initially immediate
not defferable
。名称中带有
immediate
的选项实际上是总体约束验证方面第二个最直接的选项,仅在两个 deferrable
中是第一个最直接的,我猜这就是该语法的来源。create table
、alter table..disable trigger
和set constraints
文档条目的约束部分对此不是很清楚,而且immediate的不同含义使情况变得更糟。 64.5。索引唯一性检查更有帮助(这里,立即实际上意味着立即):
表示这是一个不可延迟的唯一索引,并且必须立即进行唯一性检查,如上所述。UNIQUE_CHECK_YES
表示唯一约束是可延迟的。 PostgreSQL将使用这种模式来插入每行的索引条目。访问方法必须允许重复条目进入索引,并通过从 aminsert 返回 false 来报告任何潜在的重复项。对于返回 false 的每一行,将安排延迟重新检查。UNIQUE_CHECK_PARTIAL
表示这是对报告为潜在唯一性违规的行的延迟重新检查。UNIQUE_CHECK_EXISTING
来源中的评论也非常有帮助:
这可能是语句结束检查、提交时检查或由 SET CONSTRAINTS 命令触发的检查。
但前提是您记住默认值是真正立即的,中间语句并且它指的是计划的重新检查。基础检查仍然是立即进行的。