SQL 删除级联

示例

假设您有一个管理房间的应用程序。
进一步假设您的应用程序是基于每个客户端(租户)运行的。
您有几个客户。
因此,您的数据库将包含一个用于客户的表和一个用于房间的表。

现在,每个客户都有N个房间。

这应该意味着您的房间表上有一个外键,它引用了客户表。

ALTER TABLEdbo.T_Room WITH CHECK ADD  CONSTRAINT FK_T_Room_T_Client FOREIGN KEY(RM_CLI_ID)
REFERENCESdbo.T_Client(CLI_ID)
GO

假设客户端继续使用其他软件,则必须删除其软件中的数据。但是如果你这样做

DELETE FROM T_Client WHERE CLI_ID = x

然后,您将收到外键冲突,因为您无法在客户端仍有房间时删除客户端。

现在,您将在应用程序中编写代码,以删除客户端的房间,然后再删除客户端。进一步假设,将来,由于应用程序功能的扩展,将在数据库中添加更多外键依赖项。可怕。对于数据库中的每个修改,您都必须在N个地方修改应用程序的代码。可能还需要在其他应用程序中修改代码(例如,与其他系统的接口)。

有比在您的代码中做的更好的解决方案。
您可以只添加ON DELETE CASCADE到您的外键。

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

现在你可以说

DELETE FROM T_Client WHERE CLI_ID = x

并在删除客户端时自动删除房间。
问题已解决-无需更改应用程序代码。

请注意:在Microsoft SQL-Server中,如果您有一个引用自身的表,则此方法将无效。因此,如果您尝试在递归树结构上定义删除级联,如下所示:

IF NOT EXISTS (SELECT * FROMsys.foreign_keysWHERE 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 * FROMsys.foreign_keysWHERE 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”中的所有条目...(不再进行非增量更新)