我有一个自引用的表:
CREATE TABLE [dbo].[TestTable] (
[id] [bigint] IDENTITY(100000,1) NOT NULL,
[referenced_id] [bigint] NULL,
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED (
[id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[TestTable] WITH NOCHECK ADD CONSTRAINT [FK_ReferencedId] FOREIGN KEY ([referenced_id])
REFERENCES [dbo].[TestTable] ([id])
GO
ALTER TABLE [dbo].[TestTable] CHECK CONSTRAINT [FK_ReferencedId]
GO
假设我有几行相互引用:
id | referenced_id
-------|--------------
100023 | 100024
100024 | 100023
100025 | 100026
100026 | 100023
如果我尝试DELETE
使用该行WHERE [id] = 100023
,则 FK 将被违反,因为100024
并100026
引用该行并且该DELETE
行将失败。但是,如果我只是DELETE FROM [dbo].[TestTable]
,它似乎可以工作并成功删除所有行。因此,SQL Server 似乎只在单个DELETE
语句中要删除的所有行都被删除之后才检查 FK 约束,而不是在每行删除之间。
我可以依靠这种行为,还是DELETE
有时会失败?
是的。检查语句的约束,而不是每一行,也不是在事务结束时检查。
这是从该表中删除的查询计划。
删除发生在顶部分支上,但已删除行的 ID 是假脱机的,并且在聚集索引删除步骤之后,假脱机 ID 用于查找引用已删除行的任何行。如果发现任何一个 DELETE 将被回滚(DML 语句总是在内部的嵌套事务中运行),并引发 FK 错误。