ON DELETE CASCADE

假設你有一個管理房間的應用程式。
進一步假設你的應用程式基於每個客戶端(租戶)執行。
你有幾個客戶。
因此,你的資料庫將包含一個用於客戶端的表,一個用於房間。

現在,每個客戶都有 N 個房間。

這應該意味著你的房間表上有一個外來鍵,引用客戶端表。

ALTER TABLE dbo.T_Room  WITH CHECK ADD  CONSTRAINT FK_T_Room_T_Client FOREIGN KEY(RM_CLI_ID)
REFERENCES dbo.T_Client (CLI_ID)
GO

假設客戶轉向某些其他軟體,你將不得不刪除其軟體中的資料。但如果你這樣做

DELETE FROM T_Client WHERE CLI_ID = x 

然後你會得到一個外來鍵違規,因為當他還有房間時你不能刪除它。

現在,你已經在應用程式中編寫程式碼,在刪除客戶端之前刪除客戶端的房間。進一步假設,將來會在資料庫中新增更多外來鍵依賴項,因為應用程式的功能會擴充套件。可怕。對於資料庫中的每個修改,你都必須在 N 個位置調整應用程式的程式碼。可能你還必須在其他應用程式中調整程式碼(例如,與其他系統的介面)。

有一個比在程式碼中執行它更好的解決方案。
你只需將 ON DELETE CASCADE 新增到你的外來鍵即可。

ALTER TABLE dbo.T_Room  -- WITH CHECK -- SQL-Server can specify WITH CHECK/WITH NOCHECK
ADD  CONSTRAINT FK_T_Room_T_Client FOREIGN KEY(RM_CLI_ID)
REFERENCES dbo.T_Client (CLI_ID) 
ON DELETE CASCADE 

現在你可以說

DELETE FROM T_Client WHERE CLI_ID = x 

刪除客戶端時會自動刪除房間。
問題解決了 - 沒有應用程式程式碼更改。

需要注意的一點是:在 Microsoft SQL-Server 中,如果你有一個引用自身的表,這將不起作用。因此,如果你嘗試在遞迴樹結構上定義刪除級聯,如下所示:

IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_T_FMS_Navigation_T_FMS_Navigation]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_FMS_Navigation]'))
ALTER TABLE [dbo].[T_FMS_Navigation]  WITH CHECK ADD  CONSTRAINT [FK_T_FMS_Navigation_T_FMS_Navigation] FOREIGN KEY([NA_NA_UID])
REFERENCES [dbo].[T_FMS_Navigation] ([NA_UID]) 
ON DELETE CASCADE 
GO

IF  EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_T_FMS_Navigation_T_FMS_Navigation]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_FMS_Navigation]'))
ALTER TABLE [dbo].[T_FMS_Navigation] CHECK CONSTRAINT [FK_T_FMS_Navigation_T_FMS_Navigation]
GO

它不起作用,因為 Microsoft-SQL-server 不允許你在遞迴樹結構上使用 ON DELETE CASCADE 設定外來鍵。其中一個原因是樹可能是迴圈的,這可能會導致死鎖。

另一方面,PostgreSQL 可以做到這一點;
要求是樹是非迴圈的。
如果樹是迴圈的,你將收到執行時錯誤。
在這種情況下,你只需要自己實現刪除功能。

提醒一句:
這意味著你不能再簡單地刪除並重新插入客戶端表,因為如果這樣做,它將刪除“T_Room”中的所有條目…(不再進行非增量更新)