行估计总是太低

Pet*_*ter 6 sql-server execution-plan sql-server-2016

我有一个涉及全文搜索的查询,如下所示:

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;
Run Code Online (Sandbox Code Playgroud)

这会生成两个主要计划,一个在所有情况下都非常快,另一个在大多数情况下非常慢。

我已经尝试过这个查询,这样就没有包括 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有趣的是没有全文搜索运算符。

RANK在这种情况下,Containstable 需要生成相同的结果集,我已经使用AND RANK > 0WHERE来生成我想要的结果,从而生成此计划:https : //www.brentozar.com/pastetheplan/?id=B1U7AA2zm

我现在唯一的问题是为什么行估计仍然不准确,但我现在不太关心我的 FT 查询似乎更快更可靠。很高兴! https://www.brentozar.com/pastetheplan/?id=B1U7AA2zm

@EvanCarroll 统计直方图在这里:https ://pastebin.com/p7s0NvX5

一些后续信息 - 支持的应用程序的一些典型 FT 搜索查询的执行计划之前/之后

一种

  1. 之前:https : //www.brentozar.com/pastetheplan/?id=SJlAAAN7X(5 秒)
  2. 之后:https : //www.brentozar.com/pastetheplan/?id=H1ltkkSmm(<1 秒)

  1. 之前:https : //www.brentozar.com/pastetheplan/?id=Sy-gxJBQm(40 秒)
  2. 之后:https : //www.brentozar.com/pastetheplan/?id=Sy2VxJrm7(1 秒)

C

  1. 之前:https : //www.brentozar.com/pastetheplan/?id=r1z5e1rQ7(2秒)
  2. 之后:https : //www.brentozar.com/pastetheplan/?id=r1oplkSQm(<1 秒)

D

  1. 之前:https : //www.brentozar.com/pastetheplan/?id=B1kHf1BQQ(2分20秒)
  2. 之后:https : //www.brentozar.com/pastetheplan/?id=r1D5z1SQm(11 秒)

Kin*_*hah 4

(总结我的评论并作为答案)

查询重写将解决行估计值较低的问题。正如 Joe Chang 在他的博客文章 《查询优化器狂野》中所解释的那样 - 全文

根据 Microsoft 文档,CONTAINS 是“WHERE 子句中使用的预测”,而 CONTAINSTABLE 充当表。

使用与实际计划相比,使用使用具有低行估计的嵌套循环连接可以获得更好的计划(合并连接)CONTAINSTABLEcontains

您可以将查询重写为:

SELECT TOP 30 p.PersonId,
              p.PersonParentId,
              p.PersonName,
              p.PersonPostCode
FROM dbo.People p
left join containstable (ContactFullText, '"mr" AND "ch*"') cf on cf.[yourKey] = p.PersonId
WHERE p.PersonDeletionDate IS NULL
      AND p.PersonCustomerId = 24
      --AND CONTAINS(ContactFullText, '"mr" AND "ch*"')
      AND p.PersonGroupId IN(197, 206, 186, 198)
      AND [RANK] > 0
ORDER BY p.PersonParentId,
         p.PersonName;
Run Code Online (Sandbox Code Playgroud)