SQL Server 2014:有关不一致的自连接基数估计的任何解释?


27

考虑SQL Server 2014中的以下查询计划:

在此处输入图片说明

在查询计划中,自联接ar.fId = ar.fId产生的估计值为1行。但是,这在逻辑上是不一致的估计:ar只有20,608行,并且只有一个不同的值fId(准确地反映在统计数据中)。因此,此联接产生行(~424MMrow)的全叉积,导致查询运行几个小时。

我很难理解为什么SQL Server会提出一个很容易证明与统计数据不一致的估计。有任何想法吗?

初步调查和其他细节

根据Paul 在这里答案,似乎用于估计联接基数的SQL 2012和SQL 2014启发式方法应该可以轻松处理需要比较两个相同直方图的情况。

我从跟踪标志2363的输出开始,但无法轻松理解。下面的代码段是否表示SQL Server正在比较的直方图fIdbId以便估计仅使用的联接的选择性fId?如果是这样,那显然是不正确的。还是我误读了跟踪标志输出?

Plan for computation:
  CSelCalcExpressionComparedToExpression( QCOL: [ar].fId x_cmpEq QCOL: [ar].fId )
Loaded histogram for column QCOL: [ar].bId from stats with id 3
Loaded histogram for column QCOL: [ar].fId from stats with id 1
Selectivity: 0

请注意,我想出了几种变通办法,这些变通办法包含在完整的repro脚本中,并将此查询缩短为毫秒。这个问题的重点是了解行为,如何在以后的查询中避免它,以及确定它是否应与Microsoft一起提交。

这是完整的repro脚本,这是跟踪标志2363完整输出,这是查询和表定义,以防您想快速查看它们而不打开完整的脚本:

WITH cte AS (
    SELECT ar.fId, 
        ar.bId,
        MIN(CONVERT(INT, ar.isT)) AS isT,
        MAX(CONVERT(INT, tcr.isS)) AS isS
    FROM  #SQL2014MinMaxAggregateCardinalityBug_ar ar 
    LEFT OUTER JOIN #SQL2014MinMaxAggregateCardinalityBug_tcr tcr
        ON tcr.rId = 508
        AND tcr.fId = ar.fId
        AND tcr.bId = ar.bId
    GROUP BY ar.fId, ar.bId
)
SELECT s.fId, s.bId, s.isS, t.isS
FROM cte s 
JOIN cte t 
    ON t.fId = s.fId 
    AND t.isT = 1

CREATE TABLE #SQL2014MinMaxAggregateCardinalityBug_ar (
    fId INT NOT NULL,
    bId INT NOT NULL,
    isT BIT NOT NULL
    PRIMARY KEY (fId, bId)
)

CREATE TABLE #SQL2014MinMaxAggregateCardinalityBug_tcr (
    rId INT NOT NULL,
    fId INT NOT NULL,
    bId INT NOT NULL,
    isS BIT NOT NULL
    PRIMARY KEY (rId, fId, bId, isS)
)

Answers:


23

我很难理解为什么SQL Server会提出一个很容易证明与统计数据不一致的估计。

一致性

不能保证一致性。可以使用不同的统计方法在不同时间在不同(但逻辑上等效)子树上计算估计值。

说将这两个相同的子树连接起来应该产生叉积的逻辑没有错,但是同样也没有说推理的选择比其他任何选择都合理的逻辑。

初步估计

在您的特定情况下,不对两个相同的子树执行联接初始基数估计。当时的树形为:

  LogOp_Join
     LogOp_GbAgg
        LogOp_LeftOuterJoin
           LogOp_Get TBL:ar
           LogOp_Select
              LogOp_Get TBL:tcr
              ScaOp_Comp x_cmpEq
                 ScaOp_Identifier [tcr] .rId
                 ScaOp_Const值= 508
           ScaOp_Logical x_lopAnd
              ScaOp_Comp x_cmpEq
                 ScaOp_Identifier [ar] .fId
                 ScaOp_Identifier [tcr] .fId
              ScaOp_Comp x_cmpEq
                 ScaOp_Identifier [ar] .bId
                 ScaOp_Identifier [tcr] .bId
        AncOp_PrjList 
           AncOp_PrjEl Expr1003 
              ScaOp_AggFunc stopMax
                 ScaOp_Convert int
                    ScaOp_Identifier [tcr] .isS
     LogOp_Select
        LogOp_GbAgg
           LogOp_LeftOuterJoin
              LogOp_Get TBL:ar
              LogOp_Select
                 LogOp_Get TBL:tcr
                 ScaOp_Comp x_cmpEq
                    ScaOp_Identifier [tcr] .rId
                    ScaOp_Const值= 508
              ScaOp_Logical x_lopAnd
                 ScaOp_Comp x_cmpEq
                    ScaOp_Identifier [ar] .fId
                    ScaOp_Identifier [tcr] .fId
                 ScaOp_Comp x_cmpEq
                    ScaOp_Identifier [ar] .bId
                    ScaOp_Identifier [tcr] .bId
           AncOp_PrjList 
              AncOp_PrjEl Expr1006 
                 ScaOp_AggFunc stopMin
                    ScaOp_Convert int
                       ScaOp_Identifier [ar] .isT
              AncOp_PrjEl Expr1007 
                 ScaOp_AggFunc stopMax
                    ScaOp_Convert int
                       ScaOp_Identifier [tcr] .isS
        ScaOp_Comp x_cmpEq
           ScaOp_Identifier Expr1006 
           ScaOp_Const值= 1
     ScaOp_Comp x_cmpEq
        ScaOp_Identifier QCOL:[ar] .fId
        ScaOp_Identifier QCOL:[ar] .fId

第一输入连接已经有一个未投影聚集体简化了,并且第二连接输入具有谓词t.isT = 1推它下面,其中t.isTMIN(CONVERT(INT, ar.isT))。尽管如此,该isT谓词的选择性计算仍可以CSelCalcColumnInInterval在直方图中使用:

  CSelCalcColumnInInterval
      列:COL:Expr1006 

已从ID为3的统计信息中的QCOL列加载的直方图:[ar] .isT

选择性:4.85248e-005

统计收集生成: 
  CStCollFilter(ID = 11,CARD = 1)
      CStCollGroupBy(ID = 10,CARD = 20608)
          CStCollOuterJoin(ID = 9,CARD = 20608 x_jtLeftOuter)
              CStCollBaseTable(ID = 3,CARD = 20608 TBL:ar)
              CStCollFilter(ID = 8,CARD = 1)
                  CStCollBaseTable(ID = 4,CARD = 28 TBL:tcr)

(正确的)期望是该谓词将20,608行减少为1行。

联合估算

现在的问题是,来自另一个联接输入的20,608行将如何与这一行匹配:

  LogOp_Join
      CStCollGroupBy(ID = 7,CARD = 20608)
          CStCollOuterJoin(ID = 6,CARD = 20608 x_jtLeftOuter)
              ...

      CStCollFilter(ID = 11,CARD = 1)
          CStCollGroupBy(ID = 10,CARD = 20608)
              ...

      ScaOp_Comp x_cmpEq
          ScaOp_Identifier QCOL:[ar] .fId
          ScaOp_Identifier QCOL:[ar] .fId

通常,有几种不同的方法可以估算联接。例如,我们可以:

  • 在每个子树中的每个计划运算符处派生新的直方图,在连接处对齐它们(根据需要插入步长值),并查看它们如何匹配;要么
  • 对直方图执行更简单的“粗略”对齐(使用最小值和最大值,而不是逐步进行);要么
  • 单独为联接列计算单独的选择性(从基表中进行,无需任何过滤),然后添加非联接谓词的选择性效果。
  • ...

根据所使用的基数估计量和一些启发式方法,可以使用任何这些(或变体)。有关更多信息,请参阅《 Microsoft白皮书使用SQL Server 2014基数估计器优化查询计划》

虫子?

现在,如问题所述,在这种情况下,“简单”单列联接(在上fId)使用CSelCalcExpressionComparedToExpression计算器:

计算计划:

  CSelCalcExpressionComparedToExpression [ar] .fId x_cmpEq [ar] .fId

列QCOL:[ar] .bId(ID为2的统计信息)的已加载直方图
已加载ID为1的QCOL列的直方图:[ar] .fId

选择性:0

此计算估计将20608行与1个过滤的行连接在一起将具有零选择性:没有行将匹配(在最终计划中报告为一行)。错了吗 是的,这里的新CE可能存在错误。有人可能会争辩说1行将匹配所有行或不匹配任何行,因此结果可能是合理的,但是有理由相信其他情况。

细节实际上是相当棘手的,但是期望基于未过滤的fId直方图进行估计,并通过过滤器的选择性进行修改,从而给20608 * 20608 * 4.85248e-005 = 20608出行是非常合理的。

执行此计算将意味着使用计算器CSelCalcSimpleJoinWithDistinctCounts而不是CSelCalcExpressionComparedToExpression。没有记录的方法可以执行此操作,但是如果您好奇,可以启用未记录的跟踪标志9479:

9479计划

请注意,最后的联接从两个单行输入产生20,608行,但这并不奇怪。它与原始CE根据TF 9481制定的计划相同。

我提到细节是棘手的(调查很费时),但是据我所知,问题的根本原因与谓词有关rId = 508,选择性为零。该零估计值以通常的方式上升到一行,当它考虑输入树中的较低谓词时,这似乎有助于所讨论的连接处的零选择性估计(因此有的加载统计信息bId)。

允许外部联接保持零行内部估算(而不是提高到一行)(以便所有外部行都合格),可以使用任一计算器提供“无错误”的联接估算。如果您对此感兴趣,则未记录的跟踪标志为9473(单独):

9473计划

连接基数估计的行为CSelCalcExpressionComparedToExpression也可以修改为不考虑带有另一个未记录的变化标志的``bId''(9494)。我提到所有这些是因为我知道您对这些事情感兴趣。不是因为他们提供了解决方案。除非您向Microsoft报告此问题,然后他们解决(或不解决),否则以不同的方式表达查询可能是最好的解决方法。不管行为是否是故意的,他们都应该对回归有兴趣。

最后,整理一下复制脚本中提到的另一件事:问题计划中Filter的最终位置是基于成本的探索结果,GbAggAfterJoinSel将聚合和filter移到了join之上,因为join输出很小行数。如您所料,过滤器最初位于联接下方。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.