如何在检查约束中使用递归CTE?

时间:2019-03-31 08:36:21

标签: sql-server common-table-expression check-constraints

我正在尝试在表上创建check constraint,以使ParentID永远不会成为当前记录的后代

例如,我有一个表Categories,具有以下字段ID,Name,ParentID

我有以下CTE

WITH Children AS (SELECT ID AS AncestorID, ID, ParentID AS NextAncestorID FROM Categories UNION ALL SELECT Categories.ID, Children.ID, Categories.ParentID FROM Categories JOIN Children ON Categories.ID = Children.NextAncestorID) SELECT ID FROM Children where AncestorID =99

这里的结果是正确的,但是当我尝试将其作为约束添加到表中时,

ALTER TABLE dbo.Categories ADD CONSTRAINT CK_Categories CHECK (ParentID NOT IN(WITH Children AS (SELECT ID AS AncestorID, ID, ParentID AS NextAncestorID FROM Categories UNION ALL SELECT Categories.ID, Children.ID, Categories.ParentID FROM Categories JOIN Children ON Categories.ID = Children.NextAncestorID) SELECT ID FROM Children where AncestorID =ID))

我收到以下错误:

  

关键字“ with”附近的语法不正确。如果这个陈述是   公用表表达式,xmlnamespaces子句或更改跟踪   上下文子句,前一条语句必须以   分号。

WITH之前添加分号没有帮助。

正确的方法是什么?

谢谢!

1 个答案:

答案 0 :(得分:1)

根据column constraints上的SQL Server文档:

  

检查

     

是通过限制可以输入到一个或多个列中的可能值来强制域完整性的约束。

     

logical_expression

     

是在CHECK约束中使用的逻辑表达式,并返回TRUE或FALSE。与CHECK约束一起使用的logical_expression无法引用另一个表,但可以为同一行引用同一表中的其他列。该表达式不能引用别名数据类型。

(以上内容引用自该文档的SQL Server 2017版本,但一般原则也适用于所有以前的版本,并且您未声明要使用的版本。)

这里的重要部分是“无法引用另一个表,但可以引用相同行相同表中的其他列”(添加了重点)。 CTE将被视为另一个表格。

因此,您不能拥有像用于CHECK约束的CTE这样的复杂查询。 就像Saman所建议的那样,如果要检查其他行中的现有数据,并且该数据必须在DB层中,则可以将其作为触发器。

但是,触发器有其自身的缺点(例如,可发现性问题,那些不知道触发器存在的人所无法预料的行为。

正如萨米(Sami)在他们的评论中建议的那样,另一个选择是UDF,但这并不是它自己的问题,根据this question about this approach in SQL Server 2008的回答,它可能同时具有性能和稳定性。它可能仍同样适用于更高版本。

如果可能的话,我通常会说最好将逻辑移到应用程序层。从您的评论中可以看出,您已经具有“客户端”验证。如果在该客户端和数据库服务器之间(例如在Web应用程序中)存在一个应用程序服务器,我建议在该应用程序服务器中(而不是数据库中)放置其他验证。