SQL Server 如何知道谓词是相关的?

Jus*_*ant 15 performance sql-server statistics sql-server-2008-r2 query-performance

在诊断基数估计不佳的 SQL Server 2008 R2 查询(尽管有简单的索引、最新的统计数据等)和查询计划不佳时,我发现了一篇可能相关的知识库文章: FIX:运行查询时性能不佳包含 SQL Server 2008 或 SQL Server 2008 R2 或 SQL Server 2012 中的关联 AND 谓词

我可以猜测知识库文章中“相关”的含义,例如谓词#2 和谓词#1 主要针对相同的行。

但我不知道 SQL Server 是如何知道这些相关性的。表是否需要包含来自两个谓词的列的多列索引?SQL 是否使用统计信息来检查一列中的值是否与另一列相关?还是使用了其他方法?

我问这个有两个原因:

  1. 确定使用此修补程序可以改进我的哪些表和查询
  2. 知道我应该在索引、统计等方面做些什么来影响 #1

Pau*_*ite 20

考虑下面显示的简单AdventureWorks查询和执行计划。该查询包含与 连接的谓词AND。优化器的基数估计为41,211行:

-- Estimate 41,211 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336
    AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';
Run Code Online (Sandbox Code Playgroud)

默认执行计划

使用默认统计信息

仅给定单列统计信息,优化器通过分别估计每个谓词的基数并将结果选择性相乘来生成此估计值。这种启发式假设谓词是完全独立的。

将查询分成两部分使计算更易于查看:

-- Estimate 68,336.4 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336;
Run Code Online (Sandbox Code Playgroud)

Transaction History 表总共包含 113,443 行,因此 68,336.4 估计值表示该谓词的选择性为 68336.4 / 113443 = 0.60238533。该估计是使用TransactionID列的直方图信息和查询中指定的常量值获得的。

-- Estimate 68,413 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';
Run Code Online (Sandbox Code Playgroud)

该谓词的估计选择性为 68413.0 / 113443 = 0.60306056。同样,它是根据谓词的常量值和TransactionDate统计对象的直方图计算得出的。

假设谓词完全独立,我们可以通过将它们相乘来估计两个谓词的选择性。最终的基数估计是通过将结果选择性乘以基表中的 113,443 行获得的:

0.60238533 * 0.60306056 * 113443 = 41210.987

舍入后,这是在原始查询中看到的 41,211 估计(优化器也在内部使用浮点数学)。

不是一个很好的估计

TransactionIDTransactionDate列具有在AdventureWorks数据集密切相关(如单调递增键和日期列经常这样做)。这种相关性意味着违反了独立性假设。因此,执行后查询计划显示 68,095 行,而不是估计的 41,211 行:

执行后计划

跟踪标志 4137

启用此跟踪标志会更改用于组合谓词的启发式方法。优化器没有假设完全独立,而是认为两个谓词的选择性足够接近以至于它们很可能是相关的:

-- Estimate 68,336.4
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336
    AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13'
OPTION (QUERYTRACEON 4137);
Run Code Online (Sandbox Code Playgroud)

回想一下,TransactionID单独的谓词估计了 68,336.4 行,TransactionDate单独的谓词估计了 68,413 行。优化器选择了这两个估计中较低的一个,而不是乘以选择性。

当然,这只是一种不同的启发式方法,但可以帮助改进对具有相关AND谓词的查询的估计。每个谓词都考虑可能的相关性,当AND涉及许多子句时,还会进行其他调整,但该示例仅用于显示它的基础知识。

多列统计

这些可以帮助查询相关性,但直方图信息仍然仅基于统计信息的前导列。因此,以下候选多列统计数据在一个重要方面有所不同:

CREATE STATISTICS
    [stats Production.TransactionHistory TransactionID TransactionDate]
ON Production.TransactionHistory
    (TransactionID, TransactionDate);

CREATE STATISTICS
    [stats Production.TransactionHistory TransactionDate TransactionID]
ON Production.TransactionHistory
    (TransactionDate, TransactionID);
Run Code Online (Sandbox Code Playgroud)

仅取其中之一,我们可以看到唯一的额外信息是“全部”密度的额外级别。直方图仍然只包含有关TransactionDate列的详细信息。

DBCC SHOW_STATISTICS
    (
        'Production.TransactionHistory', 
        'stats Production.TransactionHistory TransactionDate TransactionID'
    );
Run Code Online (Sandbox Code Playgroud)

多列统计

有了这些多列统计信息......

SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336
    AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';
Run Code Online (Sandbox Code Playgroud)

...执行计划显示的估计值只有单列统计信息可用时完全相同

多列统计计划