为什么 SQL Server 使用聚集索引扫描进行自引用 FK 级联删除

Vac*_*ano 4 performance foreign-key sql-server delete sql-server-2012

我做了一个例子来让我展示我的问题是什么:

设置:

CREATE TABLE [dbo].[Test](
    [TestId] [bigint] IDENTITY(1,1) NOT NULL,
    [ParentTestId] [bigint] NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED ([TestId] ASC)
)
GO

ALTER TABLE [dbo].[Test]  WITH CHECK ADD  CONSTRAINT [FK_Test_ParentTest] 
      FOREIGN KEY([ParentTestId])
REFERENCES [dbo].[Test] ([TestId])
GO

ALTER TABLE [dbo].[Test] CHECK CONSTRAINT [FK_Test_ParentTest]
GO

DECLARE @iter INT
SET @iter = 1
WHILE @iter < 1000
BEGIN
    INSERT INTO dbo.Test ( ParentTestId )
    VALUES  ( null ),( null ),( null ),( null ),( null ),( null ),( null ),( null ),
    ( null ),( null ),( null ),( null ),( null ),( null ),( null ),( null ),( null ),
    ( null ),( null ),( null ),( null ),( null ),( null ),( null ),( null ),( null ),
    ( null ),( null ),( null ),( null ),( null ),( null ),( null ),( null ),( null ),
    ( null ),( null ),( null ),( null ),( null ),( null ),( null ),( null ),( null )
    SET @iter = @iter + 1
END
go
Run Code Online (Sandbox Code Playgroud)

这将创建一个自引用表并向其中添加超过 40,000 行。

行动:

DELETE FROM dbo.Test WHERE TestId = 200
Run Code Online (Sandbox Code Playgroud)

然后我想从这个表中删除一行。如果您打开实际的查询计划并运行上述语句,您可以看到 20% 的成本用于自引用键的聚集索引扫描。

在这种情况下这不是什么大问题,但我的真实情况在一个大表中有超过 2500 万行。

所以,我有两个问题:

  1. 为什么要进行索引扫描? 它有一个主键/聚集索引值。为什么它不做索引搜索?
  2. 我怎样才能让它做一个索引搜索?(删除一行大约需要 1 分钟。)

编辑:我认为这可能是由于 SQL Server 正在查看其他行是否引用了我正在删除的行。但是我将“强制外键约束”设置为“否”,并且仍然花费相同的成本进行聚集索引扫描。

Mar*_*ith 10

它需要验证您尝试删除的行不是现有行的父行。

您没有关于 的索引ParentTestId

所以它必须进行扫描。

CREATE NONCLUSTERED INDEX ix ON  [dbo].[Test](ParentTestId)
Run Code Online (Sandbox Code Playgroud)

然后你会看到一个搜索。

顺便说一句:在这种情况下,扫描的 20% 估计成本可能被低估了。

FK 验证是在左半连接下的,SQL Server 会花费它,好像只需要部分扫描一样,它将找到匹配的行并且删除将失败。

据推测,您实际删除的行通常会成功,因此需要进行全面扫描以验证没有冲突的行。

使用跟踪标志4138关闭行目标

DELETE FROM dbo.Test
WHERE  TestId = 200 
OPTION (querytraceon 4138 )
Run Code Online (Sandbox Code Playgroud)

重新计算成本的计划显示 CI 扫描率为 100% 而不是 20%(因为它现在假设需要进行全面扫描)

在此处输入图片说明

估计成本的这种差异足以显示缺失的索引建议。

然而,该计划中显示的成本仍然不是很有代表性。您可能会注意到它们加起来为 219%。

此外,带有和不带有跟踪标志的查询的总体计划成本都相同0.0168268。事实上,完整的 CI 扫描的成本应该是0.152373( 0.0485075 + 0.103866)

在此处输入图片说明

但它的上限似乎不超过原始计划成本(并且总体计划成本也不会向上调整,因此百分比不正确)