实际行数和估计行数相差很大

Dan*_*agg 3 sql-server execution-plan cardinality-estimates sql-server-2019 query-performance

完整的实际计划在这里。

在执行计划之前(因为我正在调试一个运行不佳的计划),我有这个变量赋值块:

DECLARE @Days INT = 180
DECLARE @DateRangeFrom DateTime = DATEADD(d, -@Days, getDate())
DECLARE @DateRangeTo DateTime = getDate()
DECLARE @FacilityID INT = 1010
DECLARE @Answer0 INT = 1879
DECLARE @Answer1 INT = 1949
DECLARE @Answer1SetID INT = 1607
DECLARE @Answer2 INT = 1907
DECLARE @Answer2SetID INT = 1593
Run Code Online (Sandbox Code Playgroud)

我的第一个问题是在 IRItemAnswer_Info 表(节点 ID 19)上执行的查找。它溢出到 Tempdb,它已经以错误的方式开始查询。它引用IRItemAnswerInfo_DGItemID_AnswerSourceID索引,这是正确的索引,因为我正在匹配DGItemIDAnswerSourceID,然后返回IncidentID。索引创建为

CREATE NONCLUSTERED INDEX IRItemAnswerInfo_DGItemID_AnswerSourceID
ON dbo.IRItemAnswer_Info (DGItemID, AnswerSourceID) 
INCLUDE([IncidentID], [AnswerBoolean])
Run Code Online (Sandbox Code Playgroud)

但是,查询的估计行数为 53,459,实际行数为 969,812。

我刚刚完成强制通过新的统计数据UPDATE STATISTICS IRItemAnswer_Info IRItemAnswerInfo_DGItemID_AnswerSourceID WITH FULLSCAN,这没有什么区别。

DBCC SHOW_STATISTICS ('IRItemAnswer_Info', 'DGItemID')对于DGItemID=1949EQ_ROWSas1,063,536

DBCC SHOW_STATISTICS ('IRItemAnswer_Info', 'AnswerSourceID')对于AnswerSourceID=1607EQ_ROWS作为970,079

数据库运行兼容级别 140 (SQL Server 2017)。我们将运行 2019 年,但在执行此操作之前,我们需要纠正存储过程中的一些问题。

接下来我应该看什么?


我选择了性能最差的输出,这是最常见的值。 IRItemAnswer_Info是一个表,其中包含与事件关联的用户定义的答案,其中DGItemID=1949是最常见的问题之一(几乎每个事件都有一个),哪里AnswerSourceID=1607是最常见的答案。鉴于它们之间存在很强的相关性,我应该如何重新排序查询?

因为这是一个有点混乱的点,所以INNER JOIN同一个表有两个 s IRItemAnswer_Info。第一个是我正在寻找的答案(由问题iria.DGItemID=1879及其输出iria.AnswerSourceID链接确定irai.AltLabel),第二个是限制因素。我只想要问题iiai1.DGItemID=1949有答案的记录iiai1.AnswerSourceID=1607

我已明确从缓存中删除计划(使用DBCC FREEPROCCACHE)并重新运行它,结果没有变化 - 哈希匹配仍然溢出。

Pau*_*ite 6

正如相关问答中所讨论的,SQL Server 如何知道谓词是相关的?默认情况下,SQL Server 假定谓词是完全独立的。

即使使用多列索引或统计信息,它也仅具有单个前导列的详细统计信息(直方图)。接下来的问题是如何组合来自两个单独谓词的两个统计直方图。

例如,假设您有一个查询WHERE c1 = x AND c2 = y。根据直方图信息计算出的选择性c1 = x为 0.2。根据单独的直方图计算出的选择性c2 = y为 0.1。

这两个谓词一起的选择性是多少?0.2?0.1?0.2×0.1?中间某个地方?

如果没有特定的附加信息,SQL Server 必须做出有根据的猜测。最初的默认设置是假设完全独立。较新的基数估计框架使用指数退避(“介于两者之间”选项)。

您的情况略有不同,因为您对多列索引中的列进行了两个相等测试,该索引附带多列统计信息。这些并不像听起来那么宏伟。我们仍然只获得前导列的直方图,但统计对象确实包含多个列的平均密度信息。

例如,(a,b,c) 上的索引将提供 (a)、(a,b) 和 (a,b,c) 的密度信息。此频率信息确实捕获了有关相关性的一些信息,但它在每个级别都是单个数字。这意味着在给定相同列数的情况下,基于频率的估计将始终产生相同的估计。

SQL Server 确实根据多列频率信息生成选择性估计,但它还根据各个列直方图(如果可用)计算选择性。直方图估计假设独立,并且不使用指数退避。

如果基于直方图的估计比基于频率的估计具有更高的选择性,则服务器选择基于直方图的估计。您的示例中似乎就是这种情况。

Plan for computation:

  CSelCalcPointPredsFreqBased
      Distinct value calculation:
          CDVCPlanLeaf
              1 Multi-Column Stats, 0 Single-Column Stats, 0 Guesses

      Individual selectivity calculations:
          CSelCalcColumnInInterval
              Column: QCOL: [IIAI].DGItemID

          CSelCalcColumnInInterval
              Column: QCOL: [IIAI].AnswerSourceID

Loaded histogram for column QCOL: [IIAI].DGItemID from stats with id 2
Loaded histogram for column QCOL: [IIAI].AnswerSourceID from stats with id 3

Cardinality using multi-column statistics 5.45574e-07 and 
with independence assumption 0.00231336. 
Picking cardinality 0.00231336

Selectivity: 0.00231336
Run Code Online (Sandbox Code Playgroud)

根据问题中的信息,个人的选择性是:

  • DGItemID = 1063536(共 19299400 个)
  • 答案源 ID = 970079(共 19299400 个)

假设独立性,AND我们将这些选择性相乘,然后乘以全表基数以产生行估计:

19299400 * (1063536/19299400 * 970079/19299400) = 53458.3427124.
Run Code Online (Sandbox Code Playgroud)

有许多内部模型变体以不同的方式处理任务。只有少数被公开记录并通过提示或跟踪标志公开。

通常,以下提示似乎会有所帮助:

SELECT
    COUNT_BIG(*)
FROM [VaxxTracker].[dbo].[IRItemAnswer_Info] AS iiai1
WHERE
    iiai1.DGItemID = 1949 
    AND iiai1.AnswerSourceID = 1607
OPTION (USE HINT('ASSUME_MIN_SELECTIVITY_FOR_FILTER_ESTIMATES'));
Run Code Online (Sandbox Code Playgroud)

文档

  • 'ASSUME_MIN_SELECTIVITY_FOR_FILTER_ESTIMATES'
    使 SQL Server 在估计过滤器的 AND 谓词时使用最小选择性生成计划以考虑完全相关性。当与 SQL Server 2012 (11.x) 及更早版本的基数估计模型一起使用时,此提示名称相当于跟踪标志 4137,并且当跟踪标志 9471 与 SQL Server 2014 (12.x) 的基数估计模型一起使用时,具有类似的效果。 ) 或更高。

不幸的是,当基数估计从使用多列统计的基于频率的计算开始时,该提示不适用。

在您的案例中,使用原始 CE 模型可能会得到更好的结果:

USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION')
Run Code Online (Sandbox Code Playgroud)