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 万行。
所以,我有两个问题:
编辑:我认为这可能是由于 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)

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