SQL Server的优化器如何估计联接表中的行数?


13

我在AdventureWorks2012数据库中运行此查询:

SELECT 
    s.SalesOrderID,
    d.CarrierTrackingNumber,
    d.ProductID,
    d.OrderQty
FROM Sales.SalesOrderHeader s 
JOIN Sales.SalesOrderDetail d 
    ON s.SalesOrderID = d.SalesOrderID
WHERE s.CustomerID = 11077

如果查看估算的执行计划,则会看到以下内容:

在此处输入图片说明

初始索引查找(右上)使用IX_SalesOrderHeader_CustomerID索引并在文字11077上进行搜索。其估计值为2.6192行。

在此处输入图片说明

如果使用DBCC SHOW_STATISTICS ('Sales.SalesOrderHeader', 'IX_SalesOrderHeader_CustomerID') WITH HISTOGRAM,则表明值11077在两个采样键11019和11091之间。

在此处输入图片说明

11019和11091之间的不同行的平均数为2.619718,或舍入为2.61972,这是为索引搜索显示的估计行的值。

我不了解的部分是针对SalesOrderDetail表的聚集索引查找的估计行数。

在此处输入图片说明

如果我运行DBCC SHOW_STATISTICS ('Sales.SalesOrderDetail', 'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID')

在此处输入图片说明

因此,SalesOrderID(我要加入)的密度为3.178134E-05。这意味着1 / 3.178134E-05(31465)等于SalesOrderDetail表中唯一SalesOrderID值的数量。

如果在SalesOrderDetail中有31465个唯一的SalesOrderID,则分布均匀,每个SalesOrderID的平均行数为121317(总行数)除以31465。平均值为3.85561

因此,如果要循环遍历的估计行数是2.61972,并且要返回的平均值是3.85561,则我认为估计行数将是2.61972 * 3.85561 = 10.10062。

但是估计的行数是11.4867。

我认为我对第二个估算值的理解是不正确的,不同的数字似乎表明了这一点。我想念什么?

Answers:


20

我认为我对第二个估算值的理解是不正确的,不同的数字似乎表明了这一点。我想念什么?

使用SQL Server 2012基数估计器,联接的选择性将驱动嵌套循环联接内侧的估计行数,而不是相反。

通过将计算的联接输出(30.0919)的估计基数除以迭代次数(2.61972),可以得出 11.4867的数字(在showplan中显示)。使用单精度浮点算法的结果是11.4867

它真的是那么简单。请注意,(逻辑)联接选择性与物理联接运算符的选择无关。无论最终使用嵌套循环,哈希或合并联接物理运算符执行联接,都保持不变。

在SQL Server 2012及更早版本中,使用SalesOrderID来自每个表的直方图(整体上)估算连接选择性(为每个直方图步骤计算,在必要时使用线性插值在步骤边界对齐之后)。还针对独立过滤器的缩放效果调整了SalesOrderIDSalesOrderHeader表格关联的直方图CustomerID

这并不是说问题中提出的替代计算从根本上来说是“错误”;它只是做出了一系列不同的假设。对于给定的逻辑运算序列,总会有不同的方法来计算或组合估计。不能完全保证应用于同一数据的不同统计方法将产生相同的答案,或者一种方法始终优于另一种方法。应用不同的统计方法导致的不一致甚至可能会出现在单个最终执行计划中,尽管这种情况很少被注意到。

附带说明一下,SQL Server 2014基数估计器采用了不同的方法来组合独立过滤器调整后的直方图信息(“粗略对齐”),从而导致此查询的最终最终估计值为10.1006行:

Plan for computation:

  CSelCalcExpressionComparedToExpression
  (QCOL: [s].SalesOrderID x_cmpEq QCOL: [d].SalesOrderID)

Loaded histogram for column QCOL: [s].SalesOrderID from stats with id 1
Loaded histogram for column QCOL: [d].SalesOrderID from stats with id 1

Stats collection generated: 

  CStCollJoin(ID=4, **CARD=10.1006** x_jtInner)
      CStCollFilter(ID=3, CARD=2.61972)
          CStCollBaseTable(ID=1, CARD=31465 TBL: Sales.SalesOrderHeader AS TBL: s)
      CStCollBaseTable(ID=2, CARD=121317 TBL: Sales.SalesOrderDetail AS TBL: d)

尽管详细的推理是不同的(即,它不是基于假定的嵌套循环实现),但这恰好与问题中的计算结果相同。

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.