SQL Server中的可延迟约束

Deferrable Constraints in SQL Server

任何版本的SQL Server都支持可延迟约束(DC)吗?

从版本8.0开始,Oracle支持可延迟约束 - 仅在提交语句组时评估的约束,而不是在插入或更新单个表时。可延迟约束不仅仅是禁用/启用约束,因为约束仍处于活动状态 - 它们仅在稍后(批处理提交时)进行评估。

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

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


OT:有一些恕我直言,SQL Server不支持,但在企业环境中有意义:

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

所有这些小东西都会使您在SQL Server中几乎无用的全尺寸RDBMS所具有的许多参照完整性和事务性功能。例如,由于不支持可延迟约束,因此"事务"作为外部一致工作单元的概念被部分否定,唯一可行的解??决方案 - 除了一些肮脏的解决方法 - 根本就没有定义参照完整性约束。我希望,事务的自然行为是你可以按照你喜欢的操作方式和顺序在其中工作,系统将确保它在你提交它时是一致的。
限制产生了类似的问题,即ON DELETE CASCADE的参照完整性约束只能以只有一个约束可以导致对象的级联删除的方式定义。这真的不适合大多数现实世界的场景。


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


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


显然不是。

我发现大约有五篇不同的博客帖子都说SQLServer(各种版本)不支持Deferrable Constraints。

另一方面,我还发现了一个帖子试图通过使用"持久计算列"来模仿这个功能,(滚动到最后一个条目),但需要注意的是


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

免责声明:模式或用例的质量在这里没有争议,它作为变通方法的基本示例给出

1
2
3
4
5
6
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是代理键的某种合适的数据类型。假设代理密钥的值由INSERT操作期间的RDBMS分配(即IDENTITY)。

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

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

现在,如果:

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

1
2
3
4
5
6
7
8
9
10
11
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语句在检查约束之前完成所有数据操作。


您可以使用此方法

1
ALTER TABLE your_table NOCHECK CONSTRAINT your_constraint

你的行动

1
ALTER TABLE your_table WITH CHECK CHECK CONSTRAINT ALL


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

  • 删除更改集定义为删除的所有外键引用,即将相应的外键列设置为NULL,或者对于使用映射表的关系,删除映射表中的DELETE条目。
  • 删除更改集定义为"已删除"的所有对象
  • 在更改集中创建所有新对象,但尚未设置外键列
  • 更新更改集中任何更新对象的所有"原始"值更改,即不更新外键列
  • 设置更改集中定义的外键列值。
  • 添加映射表映射以映射基于表的关系
  • 承诺
  • 这应该可以解决您的问题,因为所有引用的对象都可以在任何时候设置外键值...