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
索引,这是正确的索引,因为我正在匹配DGItemID
和AnswerSourceID
,然后返回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=1949
有EQ_ROWS
as1,063,536
和
DBCC SHOW_STATISTICS ('IRItemAnswer_Info', 'AnswerSourceID')
对于AnswerSourceID=1607
有EQ_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
)并重新运行它,结果没有变化 - 哈希匹配仍然溢出。
正如相关问答中所讨论的,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)
根据问题中的信息,个人的选择性是:
假设独立性,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)