簡體   English   中英

如何防止自引用表變為循環

[英]How to prevent a self-referencing table from becoming circular

這是一個非常常見的問題,但我還沒有找到我正在尋找的確切問題和答案。

我有一個表FK指向它自己的PK,以啟用任意深度層次結構,如經典tblEmployee,其列Manager是帶有PK tblEmployee.EmployeeID的FK。

讓我們在我的應用程序中說,用戶

  1. 創建新員工Alice和Dave,沒有經理,因為他們是CEO和總裁。 因此對於這兩個記錄, tblEmployee.Manager為NULL。
  2. 以Alice為經理創建新員工Bob。 然后用Bob作為他的經理創建Charles。 它們的Manager字段包含tblEmployee中另一條記錄的主鍵值。
  3. 編輯Alice的員工記錄,意思是分配Dave有她的經理(這很好),但不小心將Alice的經理設置為Charles,他是樹中Alice的兩級。

現在該表是循環引用而不是正確的樹。

確保在應用程序中無法完成步驟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小提琴

我認為這是最好的方法:

  1. 在t-sql中創建2個遞歸函數 (比臟循環執行更好),它將返回“ 給定員工的N + x個管理員 ”和“ 給定經理的Nx員工 ”的表。
  2. 防止第3步,使用GET_MANAGERS_OFGET_EMPLOYEES_OF函數將同時使用:

    • 檢查您的C#應用​​程序
    • 檢查表Employee TRIGGER (最好的安全原因,你不知道每個開發人員是否會在更新員工之前檢查,如果有人直接sql更新)

如果您分配給員工Y經理X不是Y員工Nx

在任何情況下,thoses recursives函數在您的SQL查詢和C#App中都很有用

僅供參考,有一種方法可以在C#App中處理SQL ERROR TRANSACTION(“你可以這樣做,因為......”)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM