我有一个涉及全文搜索的查询,如下所示:
SELECT TOP 30 PersonId,
PersonParentId,
PersonName,
PersonPostCode
FROM dbo.People
WHERE PersonDeletionDate IS NULL
AND PersonCustomerId = 24
AND CONTAINS(ContactFullText, '"mr" AND "ch*"')
AND PersonGroupId IN(197, 206, 186, 198)
ORDER BY PersonParentId,
PersonName;
这会生成两个主要计划,一个在所有情况下都非常快,另一个在大多数情况下非常慢。
我对这个查询进行了试验,因此不包括 FT 搜索,我发现行估计总是比它们应该的低得多。
如果我运行,update statistics...with fullscan
我仍然会从执行计划中的 NC 索引查找操作中看到极其不准确的行估计。
当行估计足够低时,选择循环连接,这通常非常慢(30 多秒)。较高的估计似乎会产生一个涉及合并连接而不是循环连接的好计划。
尽管仍然有最新的统计信息,为什么 SQL Server 仍然不估计行数?
计划:https ://www.brentozar.com/pastetheplan/?id=rkXtE0jzX
当我删除该CONTAINS
部分,从而省略全文搜索时,查询速度很快,但索引查找的行估计仍然是 1 估计,实际 2195。
在@Kin 的建议下,我使用了 CONTAINSTABLE,它立即运行并生成了以下计划:https ://www.brentozar.com/pastetheplan/?id=S1hKainzQ 有趣的是没有全文搜索运算符。
在这种情况下, Containstable 需要RANK
生成相同的结果集,我用AND RANK > 0
它WHERE
来生成我想要的结果,它生成了这个计划:https ://www.brentozar.com/pastetheplan/?id=B1U7AA2zm
我现在唯一的问题是为什么行估计仍然不准确,但我现在不那么关心了,因为我的 FT 查询看起来更快、更可靠。很高兴! https://www.brentozar.com/pastetheplan/?id=B1U7AA2zm
@EvanCarroll 统计直方图在这里:https ://pastebin.com/p7s0NvX5
一些后续信息——针对所支持的应用程序的一些典型 FT 搜索查询的执行计划之前/之后
一个
- 之前:https ://www.brentozar.com/pastetheplan/?id=SJlAAAN7X (5 秒)
- 之后:https ://www.brentozar.com/pastetheplan/?id=H1ltkkSmm (<1 秒)
乙
- 之前:https ://www.brentozar.com/pastetheplan/?id=Sy-gxJBQm (40 秒)
- 之后:https ://www.brentozar.com/pastetheplan/?id=Sy2VxJrm7 (1 秒)
C
- 之前:https ://www.brentozar.com/pastetheplan/?id=r1z5e1rQ7 (2 秒)
- 之后:https ://www.brentozar.com/pastetheplan/?id=r1oplkSQm (<1 秒)
丁
(总结我的评论并作为答案)
查询重写将解决获得低行估计的问题。正如 Joe Chang 在他的博客文章 Query Optimizer Gone Wild - Full-Text中解释的那样
使用uses a nested loop join with low row estimates与实际计划相比,您获得了更好的计划(合并连接)。
CONTAINSTABLE
contains
您可以将查询重写为:
全文查询根据 contains 子句中的文本进行部分重新编译。(根据经验)我将冒险猜测 SQL Server 期望来自关系谓词的行数较少,并且正在对 FTS 引擎执行 for-each 循环“查找”。搜索可能是性能杀手。
如果您想要可预测的性能,那么您可以将查询分成两部分。
你不应该必须这样做,但它有效。