不可延迟与立即可延迟


90

DEFERRABLE在“ 数据库系统-完整书”中阅读了有关SQL关键字的内容。

后者的[NOT DEFERRABLE]是缺省值,表示每次执行数据库修改语句时,如果修改可能违反外键约束,则紧接着检查该约束。

但是,如果我们声明一个约束为DEFERRABLE,那么我们可以选择让它等到事务完成后再检查约束。

我们按照关键字DEFERRABLE通过两种INITIALLY DEFERRED或者INITIALLY IMMEDIATE。在前一种情况下,检查将推迟到每个事务提交之前进行。在后一种情况下,将在每个语句之后立即进行检查。

如何NOT DEFERRABLE不同DEFERRABLE INITIALLY IMMEDIATE?在这两种情况下,似乎在每个单独的语句之后都会检查任何约束。

Answers:


72

有了它们,DEFERRABLE INITIALLY IMMEDIATE您可以在需要时推迟需求约束。

如果您通常要在语句时检查约束,但是对于例如批加载要推迟检查直到提交时,这很有用。

但是,对于各种DBMS,如何推迟约束的语法是不同的。

有了NOT DEFERRABLE您,您将永远无法将检查推迟到提交时间之前。


1
@romkyns:DEFERRABLE表明设计者的意图是推迟约束是值得或必要的动作。对于绝大多数数据库约束和标记来说,情况并非如此,因为它们DEFERRABLE将失去这种有用的区别。
2011年

4
@onedaywhen我的一位同事此后指出了一个很好的正当理由:实际上,您可以在任何交易的任何时候都依赖不可延期的约束,但是只有在交易开始时才能确定可延期的约束。
罗曼·斯塔科夫

@RomanStarkov另外,这也是性能问题。NOT DEFERRABLE通常最快。
Teejay

46

除了其他(正确)答案外,在谈到PostgreSQL时,还必须指出:

  • 使用NOT DEFERRABLE,在插入/更新时检查每一行

  • 使用DEFERRABLE(当前为IMMEDIATE),在插入/更新结束时检查所有行

  • 使用DEFERRABLE(当前为DEFERRED),在事务结束时检查所有行

因此,将DEFERRABLE约束设置为IMMEDIATE时,它的行为类似于NOT DEFERRABLE约束,这是不正确的


让我们详细说明一下这一区别:

CREATE TABLE example(
    row integer NOT NULL,
    col integer NOT NULL,
    UNIQUE (row, col) DEFERRABLE INITIALLY IMMEDIATE
);

INSERT INTO example (row, col) VALUES (1,1),(2,2),(3,3);

UPDATE example SET row = row + 1, col = col + 1;

SELECT * FROM example;

正确输出:

输出

但是如果我们删除DEFERRABLE INITIALLY IMMEDIATE指令,

错误:重复的键值违反了唯一约束“ example_row_col_key”详细信息:键(“ row”,col)=(2,2)已存在。**********错误**********

错误:重复的键值违反了唯一约束“ example_row_col_key” SQL状态:23505详细信息:键(“ row”,col)=(2,2)已存在。


附录 (2017年10月12日)

此行为确实记录 “兼容性”部分:

而且,PostgreSQL会立即检查不可延展的唯一性约束,而不是在标准声明的结尾处检查。


有趣。在我看来,基本上没有理由比“ IMMEDIATE”更喜欢“ NOT DEFERRABLE”行为,并且Postgres更宽容并且使NOT DEFERRABLE像“ IMMEDIATE”那样有意义。据我所知,在UPDATE语句中行的更新顺序甚至都没有记载或指定,所以这种中间语句约束检查的行为是否至少未部分指定?我看不到为什么有人会想要这种行为-但也许有一个合理的用例,我缺乏想象力。
马克·阿默里

1
是的,基本上,首选的唯一原因NOT DEFERRABLE是速度(请参见此处非延迟唯一性约束“请注意,这可能比立即唯一性检查慢得多”)。
Teejay

另外,DEFERRABLE约束不能在其他表中作为外键引用(请参见此处的参数部分,“被引用的列必须是被引用表中不可延迟的唯一或主键约束的列”)。
Teejay

1
因此,根据经验,如果在检查约束时对业务逻辑不是很重要,那么我是否应该默认使用NOT DEFERRABLE它,因为它的性能更好?
dvtan

1
在我们的ORM中,我们现在使用以下规则:1)如果约束是PK,则使用NOT DEFERRABLE2)如果至少一个FK引用约束,则我们也使用NOT DEFERRABLE3)在其他情况下,我们使用DEFERRABLE INITIALLY IMMEDIATE。这可能会稍微降低这些约束的性能,但会确保与我们使用的其他DBMS(Oracle,SqlServer)具有最大的兼容性。PK和FK并不是问题,因为我们从不更新它们的值(我认为这是DB的良好编程习惯)。
Teejay

29

除了可以延期的明显表现外,差异实际上是性能。如果没有性能损失,那么就没有必要选择是否推迟,所有约束都将是推迟的。

在知道如何限制数据的情况下,性能损失与数据库可以执行的优化有关。例如,如果约束是可延迟的,则在Oracle中为支持唯一约束而创建的索引不能是唯一索引,因为必须允许临时允许重复。但是,如果约束不可推迟,则索引可以是唯一的。


7

我参加聚会很晚了,但是我想补充一点-截至2018年12月-我知道的只有两个数据库(可能还有更多)提供了该标准SQL功能的某种实现水平:

Database    NOT DEFERRABLE  DEFERRABLE           DEFERRABLE 
                            INITIALLY IMMEDIATE  INITIALLY DEFERRED
----------  --------------  -------------------  ------------------
Oracle      N/A *1          Yes (default)        Yes
PostgreSQL  Yes (default)   Yes                  Yes
DB2         -               -                    -
SQL Server  -               -                    -
MySQL       -               -                    -
MariaDB     -               -                    -
SAP Sybase  -               -                    -
HyperSQL    -               -                    -
H2          -               -                    -
Derby       -               -                    -

* 1即使Oracle 12c接受NOT DEFERRABLE 约束状态,它实际上也将其忽略并使其工作为DEFERRABLE INITIALLY IMMEDIATE

如您所见,Oracle没有实现第一种类型(NOT DEFERRABLE),这就是为什么使用Oracle(在本例中为OP)的开发人员可能会感到困惑,并认为前两种类型等效。

有趣的是,Oracle和PostgreSQL具有不同的默认类型。也许对性能有影响。


4

NOT DEFERRABLE-您无法更改约束检查,oracle在每个语句之后(即,在insert语句之后)直接检查它。

可立即初始化-oracle在每个语句之后检查约束。但是,您可以将其更改为每个事务之后(即提交之后):

set constraint pk_tab1 deferred;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.