[英]How to prevent a self-referencing table from becoming circular
這是一個非常常見的問題,但我還沒有找到我正在尋找的確切問題和答案。
我有一個表FK指向它自己的PK,以啟用任意深度層次結構,如經典tblEmployee,其列Manager
是帶有PK tblEmployee.EmployeeID的FK。
讓我們在我的應用程序中說,用戶
tblEmployee.Manager
為NULL。 tblEmployee
中另一條記錄的主鍵值。 現在該表是循環引用而不是正確的樹。
確保在應用程序中無法完成步驟3的最佳方法是什么 ? 我只需要確保它將拒絕執行最后一次SQL更新,而是顯示一些錯誤消息。
我不是在挑剔它是SQL Server中的數據庫約束(必須在2008年或2012年工作),還是在我的C#app的業務邏輯層中使用某種驗證例程。
您可以使用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
發生更改,它就會觸發。
您可以添加“級別”整數列。
Alice和Dave將具有級別== 0如果您將為員工設置經理他的(員工)級別將是他的經理級別+ 1。
在更新期間您應該檢查經理級別是否小於員工級別...
這比使用程序更快......
您可以在UPDATE
語句中包含一個檢查:
DECLARE @Employee INT = 2
,@NewManager INT = 4
;WITH cte AS (SELECT *
FROM tblEmployee
WHERE Manager = @Employee
UNION ALL
SELECT a.*
FROM tblEmployee a
JOIN cte b
ON a.manager = b.EmployeeID)
UPDATE a
SET a.Manager = @NewManager
FROM tblEmployee a
WHERE EmployeeID = @Employee
AND NOT EXISTS (SELECT *
FROM cte b
WHERE a.EmployeeID = b.Manager)
演示: SQL小提琴
我認為這是最好的方法:
防止第3步,使用GET_MANAGERS_OF
和GET_EMPLOYEES_OF
函數將同時使用:
如果您分配給員工Y的經理X不是Y的員工Nx 。
在任何情況下,thoses recursives函數在您的SQL查詢和C#App中都很有用
僅供參考,有一種方法可以在C#App中處理SQL ERROR TRANSACTION(“你可以這樣做,因為......”)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.