如何防止自引用表变为循环

Eas*_*ere 13 c# sql sql-server circular-reference

这是一个非常常见的问题,但我还没有找到我正在寻找的确切问题和答案.

我有一个表有一个FK指向它自己的PK的表,以启用任意深层次结构,就像经典的tblEmployee一样,它的列Manager是带有PK tblEmployee.EmployeeID的FK.

让我们在我的应用程序中说,用户

  1. 创建新员工Alice和Dave,没有经理,因为他们是CEO和总裁.所以tblEmployee.ManagerNULL的这两个记录.
  2. 以Alice为经理创建新员工Bob.然后用Bob作为他的经理创建Charles.它们的Manager字段包含另一条记录的主键值tblEmployee.
  3. 编辑Alice的员工记录,意思是分配Dave有她的经理(这很好),但不小心将Alice的经理设置为Charles,他是树中Alice的两级.

现在该表是循环引用而不是正确的树.

确保在应用程序中无法完成步骤3 的最佳方法是什么?我只需要确保它将拒绝执行最后一次SQL更新,而是显示一些错误消息.

我不是在挑剔它是SQL Server中的数据库约束(必须在2008年或2012年工作),还是在我的C#app的业务逻辑层中使用某种验证例程.

mel*_*okb 17

您可以使用CHECK CONSTRAINT该验证经理ID不是循环.您不能在检查约束中具有复杂查询,但如果首先将其包装在函数中,则可以:

create function CheckManagerCycle( @managerID int )
returns int
as
begin

    declare @cycleExists bit
    set @cycleExists = 0

    ;with cte as (
        select E.* from tblEmployee E where ID = @managerID
        union all
        select E.* from tblEmployee E join cte on cte.ManagerID = E.ID and E.ID <> @managerID
    )
    select @cycleExists = count(*) from cte E where E.ManagerID = @managerID

    return @cycleExists;

end
Run Code Online (Sandbox Code Playgroud)

然后你可以使用这样的约束:

alter table tblEmployee
ADD CONSTRAINT chkManagerRecursive CHECK ( dbo.CheckManagerCycle(ManagerID) = 0 )
Run Code Online (Sandbox Code Playgroud)

这将阻止添加或更新记录以从任何来源创建循环.


编辑:一个重要的注意事项:检查约束在它们引用的列上得到验证.我最初将其编码为检查员工ID的周期,而不是经理ID.但是,这不起作用,因为它仅在更改ID列时触发.此版本确实有效,因为它会在任何时候触发ManagerID更改.