AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 141220
Accepted
Geoff Patterson
Geoff Patterson
Asked: 2016-06-15 10:55:26 +0800 CST2016-06-15 10:55:26 +0800 CST 2016-06-15 10:55:26 +0800 CST

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

  • 772

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

在此处输入图像描述

在查询计划中,自联接ar.fId = ar.fId产生 1 行的估计值。然而,这是一个逻辑上不一致的估计:ar有20,608行和只有一个不同的值fId(准确反映在统计数据中)。因此,此连接会生成行 (rows) 的完整叉积~424MM,从而导致查询运行数小时。

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

初步调查和其他细节

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

我从跟踪标志 2363 的输出开始,但没那么容易理解。以下代码片段是否意味着 SQL Server 正在比较fId和bId的直方图以估计仅使用的联接的选择性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

请注意,我已经提出了几种解决方法,它们包含在完整的重现脚本中,并将此查询缩短到毫秒。这个问题的重点是了解行为,如何在以后的查询中避免它,并确定它是否是应该向 Microsoft 提交的错误。

这是完整的重现脚本,这是跟踪标志 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)
)
sql-server performance
  • 1 1 个回答
  • 797 Views

1 个回答

  • Voted
  1. Best Answer
    Paul White
    2016-06-18T04:03:43+08:002016-06-18T04:03:43+08:00

    我很难理解为什么 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 整数
                        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 整数
                           ScaOp_Identifier [ar].isT
                  AncOp_PrjEl Expr1007
                     ScaOp_AggFunc stopMax
                        ScaOp_Convert 整数
                           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.isTis MIN(CONVERT(INT, ar.isT))。尽管如此,isT谓词的选择性计算仍可用于CSelCalcColumnInInterval直方图:

      CSelCalcColumnInInterval
          列:COL:Expr1006
    
    已加载列 QCOL 的直方图:[ar].isT 来自 id 为 3 的统计信息
    
    选择性:4.85248e-005
    
    生成的统计信息集合:
      CStCollFilter(ID=11,卡片=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,卡片=1)
              CStCollGroupBy(ID=10, CARD=20608)
                  ...
    
          ScaOp_Comp x_cmpEq
              ScaOp_Identifier QCOL:[ar].fId
              ScaOp_Identifier QCOL:[ar].fId
    

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

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

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

    漏洞?

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

    计算计划:
    
      CSelCalcExpressionComparedToExpression [ar].fId x_cmpEq [ar].fId
    
    已加载列 QCOL 的直方图:[ar].bId 来自 id 为 2 的统计信息
    已加载列 QCOL 的直方图:[ar].fId 来自 id 为 1 的统计信息
    
    选择性:0
    

    此计算评估将 20,608 行与第 1 个过滤行连接起来的选择性为零:没有行会匹配(在最终计划中报告为一行)。这是错误的吗?是的,这里可能是新 CE 中的错误。有人可能会争辩说 1 行将匹配所有行或不匹配,因此结果可能是合理的,但有理由相信并非如此。

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

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

    9479计划

    请注意,最终连接从两个单行输入中生成 20,608 行,但这并不奇怪。它与 TF 9481 下的原始 CE 制定的计划相同。

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

    允许外部连接保持零行内侧估计(而不是提高到一行)(因此所有外部行都符合条件)可以使用任一计算器提供“无错误”连接估计。如果您有兴趣探索这个,未记录的跟踪标志是 9473(单独):

    9473计划

    连接基数估计的行为CSelCalcExpressionComparedToExpression也可以修改为不考虑bId另一个未记录的变体标志 (9494)。我提到所有这些是因为我知道你对这些事情感兴趣;不是因为他们提供了解决方案。在您向 Microsoft 报告问题并且他们解决(或不解决)之前,以不同方式表达查询可能是最好的前进方式。不管该行为是否有意,他们都应该有兴趣了解回归。

    最后,整理一下复制脚本中提到的另一件事:过滤器在问题计划中的最终位置是基于成本的探索GbAggAfterJoinSel将聚合和过滤器移动到连接上方的结果,因为连接输出具有如此小的行数。如您所料,筛选器最初位于连接下方。

    • 24

相关问题

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

  • 我在哪里可以找到mysql慢日志?

  • 如何优化大型数据库的 mysqldump?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve