SQL Server如何知道谓词之间的关联?


15

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

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

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

我问这个有两个原因:

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

Answers:


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';

默认执行计划

使用默认统计

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

将查询分为两部分,使计算更容易看清:

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

交易历史记录表总共包含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';

该谓词的估计选择性为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);

回想一下,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);

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

DBCC SHOW_STATISTICS
    (
        'Production.TransactionHistory', 
        'stats Production.TransactionHistory TransactionDate TransactionID'
    );

多栏统计

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

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';

...执行计划显示的估计仅提供单列统计信息时完全相同

多列统计计划

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.