这是一个很常见的问题,但我还没有找到我正在寻找的确切问题和答案。
我有一个表有一个 FK 指向它自己的 PK,以启用任意深度的层次结构,比如经典的 tblEmployee 有一个列 Manager
这是一个 FK 与 PK tblEmployee.EmployeeID .
假设在我的应用中,用户
- 任命新员工 Alice 和 Dave,没有经理,因为他们是 CEO 和总裁。所以
tblEmployee.Manager
对于这两条记录是 NULL。 - 创建新员工鲍勃,爱丽丝担任经理。然后创建 Charles,Bob 作为他的经理。他们的经理字段包含
tblEmployee
中另一条记录的主键值。 - 为 Alice 编辑员工记录,这意味着指定 Dave 有她的经理(这很好)但不小心将 Alice 的经理设置为 Charles,他在树中比 Alice 低两级。
现在这个表是一个循环引用而不是一个正确的树。
确保第 3 步无法在应用程序中完成的最佳方法是什么?我只需要确保它会拒绝执行最后的 SQL 更新,而是显示一些错误消息。
对于它是 SQL Server 中的数据库约束(必须在 2008 年或 2012 年工作)还是我的 C# 应用程序的业务逻辑层中的某种验证例程,我并不挑剔。
最佳答案
您可以使用 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
然后你可以使用这样的约束:
alter table tblEmployee
ADD CONSTRAINT chkManagerRecursive CHECK ( dbo.CheckManagerCycle(ManagerID) = 0 )
这将阻止添加或更新记录以从任何来源创建循环。
编辑:重要说明:检查约束在它们引用的列上进行验证。我最初对此进行编码是为了检查员工 ID 的周期,而不是经理 ID。但是,这不起作用,因为它仅在 ID 列发生更改时触发。这个版本确实有效,因为它会在 ManagerID
更改时触发。
关于c# - 如何防止自引用表变成循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23019384/