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”中的所有条目…(不再进行非增量更新)