SQL Server 中的可延迟约束

Posted

技术标签:

【中文标题】SQL Server 中的可延迟约束【英文标题】:Deferrable Constraints in SQL Server 【发布时间】:2010-11-03 03:24:52 【问题描述】:

是否有任何版本的 SQL Server 支持可延迟约束 (DC)?

自 8.0 版起,Oracle has supported deferrable constraints - 仅在您提交语句组时评估约束,而不是在您插入或更新单个表时评估。可延迟约束与仅禁用/启用约束不同,因为约束仍然处于活动状态 - 它们只是稍后评估(提交批处理时)。

DC 的好处是它们允许对单独非法的更新进行评估,从而累积产生有效的最终状态。一个示例是在两行之间的表中创建循环引用,其中每一行都需要一个值存在。没有单独的插入语句会通过约束 - 但组可以。

为了阐明我的目标,我希望将 C# 中的 ORM 实现移植到 SQLServer - 不幸的是,该实现依赖于 Oracle DC 来避免计算行间的插入/更新/删除顺序。

【问题讨论】:

你基本上是在问this question的变体吗? 【参考方案1】:

到目前为止,SQL Server 不支持它们。你要解决什么问题?

【讨论】:

我们的业务系统中有一个 ORM 层,它利用了我们可能希望移植到 SQL Server 的 Oracle 中的 DC。不幸的是,似乎不支持 DC,这使移植实现的工作变得复杂。特别是,更改记录网络需要以非常特殊的(并且难以计算的顺序)进行处理,以避免违反任何 RI 约束。只是在寻找一种避免这样做的方法。 前段时间我写了一篇关于它的小帖子。基本上,您将草稿行保存在所有表中,然后将它们标记为已完成,此时 RI 会打开。谷歌搜索“使用持久计算列模拟可延迟约束。”【参考方案2】:

显然不是。

我发现大约五篇不同的博客文章都说 SQLServer(不同版本)不支持可延迟约束。

另一方面,我还发现一篇帖子试图通过使用"persisted computed columns,"(滚动到最后一个条目)来模仿此功能,但请注意

【讨论】:

链接好像坏了?【参考方案3】:

听起来您遇到的问题是 SQL 不支持 Date 和 Darwen 所说的“多重赋值”。标准 SQL 对此的回应是 SQL Server 不支持的“可延迟约束”。 SQL Server FK 或 CHECK 约束可以用 NOCHECK 标记,但并不完全相同。有关详细信息,请参阅 MSDN:ALTER TABLE (Transact-SQL)。

【讨论】:

【参考方案4】:

OT:恕我直言,SQL Server 不支持很多东西,但在企业环境中是有意义的:

此处提到的可延迟约束 MARS:为什么需要为完全自然的事物设置选项? CASCADE DELETE 约束:对于给定的 CASCADE DELETE 约束,SQL Server 只允许一个单一的级联路径。同样,我看不出为什么不允许它通过多个可能的路径在删除时级联:最后,在它真正执行的时候,总是只有一个路径被实际使用,所以为什么这是限制吗? 防止单个 ADO.NET 连接上的并行事务。 强制在连接上执行的每个命令都有要在此事务中执行的事务。 创建 UNIQUE 索引时,NULL 被视为实际值,并且只允许在索引中出现一次。但是,SQL 将 NULL 视为“未知值”的概念表明,在创建索引时会完全忽略 NULL 值...

所有这些小事使您期望从全尺寸 RDBMS 中获得的许多引用完整性和事务功能在 SQL Server 中几乎毫无用处。例如,由于不支持可延迟约束,“事务”作为外部一致的工作单元的概念被部分否定,唯一可行的解​​决方案——除了一些肮脏的变通方法——根本不定义参照完整性约束。我希望,事务的自然行为是您可以按照您喜欢的方式和操作顺序在其中工作,并且系统将确保它在您提交时是一致的。 类似的问题来自限制,即 ON DELETE CASCADE 的引用完整性约束只能以只有一个单一约束可以导致对象的级联删除的方式定义。这确实不适合大多数现实世界的场景。

【讨论】:

您现在可以使用过滤索引获得包含空值的唯一索引。 完整的事实,不幸的是,SQL Server 不允许您在其上编写好的 ORM :( SQL Server 2016 及更高版本(可能还有更早的版本)支持 MARS 和过滤索引。【参考方案5】:

如果您有自己的 ORM 层,解决问题的一种方法是通过 ORM 层的逻辑将对象更新与引用更新分开。 然后,您的 ORM 将根据您的客户端更改集分几个步骤处理事务:

    删除由您的更改集定义为被删除的所有外键引用,即将对应的外键列设置为 NULL,或者,对于使用映射表的关系,从映射表中适当地删除条目。 删除所有被您的更改集定义为“已删除”的对象 在您的更改集中创建所有新对象,但尚未设置外键列 更新更改集中任何已更新对象的所有“原始”值更改,即不更新外键列 设置更改集中定义的外键列值。 添加映射表映射以映射基于表的关系 提交

这应该可以解决您的问题,因为在任何时候设置外键值时,引用的所有对象都存在...

【讨论】:

或使用存储过程并将其插入 ORM 中该实体的 Insert() Update() (实体框架和 Linq to SQL 允许这样做***.com/questions/5346601/stored-procedures-and-orms)【参考方案6】:

有一种方法可以解决缺少的延迟约束强制执行在某些条件下(截至 2017 年 1 月,SQL Server 中不支持延迟约束)。考虑以下数据库架构:

免责声明:模式或用例的质量在这里不讨论,它是作为解决方法的基本示例给出的

CREATE TABLE T (Id TYPE NOT NULL PRIMARY KEY, NextId TYPE NOT NULL);

ALTER TABLE T WITH CHECK ADD CONSTRAINT FK_T2T 
FOREIGN KEY (NextId) REFERENCES T (Id);

CREATE UNIQUE NONCLUSTERED INDEX UC_T ON T (NextId);

其中 TYPE 是代理键的一些合适的数据类型。假设代理键的值由 RDBMS 在 INSERT 操作期间分配(即 IDENTITY)。

用例是用 NextId = NULL 保持实体 T 的“最新”版本,并通过维护单链表 T.NextId -> T.Id 来存储以前的版本。

显然,给定的模式会受到延迟约束问题的影响,因为新的“最新”版本的插入必须先于旧的“最新”版本的更新,并且在此期间数据库中将有两条记录相同的 NextId 值。

现在,如果:

主键的数据类型不必是数字,可以提前计算(即UNIQUEIDENTIFIER),然后使用MERGE语句回避延迟约束问题,如下:

DECLARE @MergeTable TABLE (Id UNIQUEIDENTIFIER);

DECLARE @NewLatestVersion UNIQUEIDENTIFIER = NEWID();

INSERT INTO @MergeTable (Id) VALUES (@NewLatestVersion);
INSERT INTO @MergeTable (Id) VALUES (@OldLatestVersion);

MERGE INTO T
USING @MergeTable m ON T.Id = m.Id
WHEN MATCHED THEN UPDATE SET T.NextId = @NewLatestVersion
WHEN NOT MATCHED THEN INSERT (Id) VALUES (@NewLatestVersion);

显然,MERGE 语句在检查约束之前完成了所有数据操作。

【讨论】:

【参考方案7】:

你可以用这个方法

ALTER TABLE your_table NOCHECK CONSTRAINT your_constraint

你的行动

ALTER TABLE your_table WITH CHECK CHECK CONSTRAINT ALL

【讨论】:

这适用于批量插入。但是我认为它总是会重新评估所有记录,因此它不适合 OLTP 工作负载。

以上是关于SQL Server 中的可延迟约束的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 存储过程中的可选参数?

SQL Server 唯一约束中的 Unicode

SQL Server中的六种约束:主键约束,外键约束,唯一约束,非空约束,检查约束,默认约束

与存储在 SQL Server 中的会话的列表的可序列化问题

为 sql server 数据库中的键、约束生成 drop-create sql 脚本

为啥我不能将 DBNull.Value 插入到 sql server 2005 中的可为空的图像字段中?