关于this answer,我偶然发现了一个无法解释的现象。
版本:
在x86_64-unknown-linux-gnu上的PostgreSQL 9.1.2,由gcc-4.4.real编译(Debian 4.4.5-8)4.4.5,64位
考虑以下演示。测试平台:
CREATE TEMP TABLE t (
id integer
,txt text
,CONSTRAINT t_pkey PRIMARY KEY (id) DEFERRABLE INITIALLY IMMEDIATE
);
INSERT INTO t VALUES
(1, 'one')
,(2, 'two');
UPDATE t
SET id = t_old.id
FROM t t_old
WHERE (t.id, t_old.id) IN ((1,2), (2,1));
目前的实施似乎有一个错误?上面的UPDATE虽然不应该,但仍有效。约束定义为INITIALLY IMMEDIATE
,我没有使用SET CONSTRAINTS
。
我错过了什么或者这是一个(相当无害的)错误?
因此,修改CTE的数据也有效,尽管它失败了NOT DEFERRED
pk:
WITH x AS (
UPDATE t SET id = 1 WHERE id = 2
)
UPDATE t SET id = 2 WHERE id = 1;
我引用manual on CTEs:
WITH中的子语句彼此同时执行 并与主要查询。因此,在使用数据修改时 WITH中的语句,实际指定更新的顺序 发生是不可预测的。所有语句都以相同的方式执行 快照(见第13章),因此他们无法“看到”彼此的影响 在目标表上。
如果没有SET CONSTRAINTS
,则会因UNIQUE违规而失败 - 正如所料:
BEGIN;
-- SET CONSTRAINTS t_pkey DEFERRED;
UPDATE t SET id = 2 WHERE txt = 'one';
UPDATE t SET id = 1 WHERE txt = 'two';
COMMIT;
答案 0 :(得分:30)
我记得当PG9处于阿尔法状态时已经提出了几乎相同的观点。以下是Tom Lane(备受瞩目的PG核心开发人员)的回答: http://archives.postgresql.org/pgsql-general/2010-01/msg00221.php
总之:不会修复。
不是说我同意你的建议,即目前的行为是一个错误。从相反的角度看待它:NOT DEFERRABLE
的行为是不正确的。
实际上,在任何情况下都不应该发生此UPDATE中的约束违规,因为在UPDATE结束时满足约束。命令结束时的状态才是最重要的。执行单个语句期间的中间状态不应向用户公开。
似乎PostgreSQL通过在每一行更新后检查重复项来实现非延迟约束,并在第一次重复时立即失败,这实质上是有缺陷的。但这是一个已知问题,可能与PostgreSQL一样古老。 现在,解决方法恰好是使用DEFERRABLE约束。而且有一些讽刺意味着你认为它不足以因为它不会失败,而不知何故它应该首先解决失败的问题!
NOT DEFERRABLE
UNIQUE
或PRIMARY KEY
约束在每行后检查。
DEFERRABLE
(IMMEDIATE
或通过INITIALLY IMMEDIATE
)的 SET CONSTRAINTS
约束在每个声明后检查。
DEFERRABLE
(DEFERRED
或通过INITIALLY DEFERRED
)的 SET CONSTRAINTS
约束在每笔交易后检查。
请注意UNIQUE
/ PRIMARY KEY
限制的特殊处理。
引用CREATE TABLE
的手册页:
在每个命令之后,会立即检查不可延迟的约束。
虽然它在Non-deferred uniqueness constraints
下的兼容性部分进一步说明:
当
UNIQUE
或PRIMARY KEY
约束无法推迟时,PostgreSQL 无论何时插入行,立即检查唯一性 改性。 SQL标准说应该强制执行唯一性 仅在声明的最后;这对于 例如,单个命令更新多个键值。获得 符合标准的行为,将约束声明为DEFERRABLE
但是 不推迟(即INITIALLY IMMEDIATE
)。请注意,这可以 明显慢于直接唯一性检查。
大胆强调我的。
如果您需要任何FOREIGN KEY
个约束来引用列,DEFERRABLE
不是一个选项,因为(per documentation):
引用的列必须是不可延迟的唯一列 或引用表中的主键约束。
答案 1 :(得分:4)
这里可能存在轻微的文档错误,但不适用于您正在展示的情况。如果您开始一个事务并且一次尝试更新,它们就会失败,但是如果单个语句使事情处于良好状态,它就不会抱怨。文档说:
如果约束是可延迟的,则此子句指定默认时间 检查约束。如果约束是INITIALLY IMMEDIATE,那么 在每个陈述后检查。这是默认值。如果 约束是最初的延迟,只在结束时检查 事务。
这正是正在发生的事情。鉴于DEFERRABLE
的文档,部分说明了
将立即检查不可延迟的约束 每一个命令。
如果没有DEFERRABLE INITIALLY IMMEDIATE
选项,示例更新将失败,即使UPDATE
语句(可能构成“命令”)使事物处于良好状态。也许应该修改文档,以便在声明每行被修改时强制执行NOT DEFERRABLE
约束