考虑下面显示的简单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估计(优化器还在内部使用浮点数学)。
不太好估计
在TransactionID
与TransactionDate
列具有在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';
...执行计划显示的估计与仅提供单列统计信息时完全相同:
Statistics objects on multiple columns also store statistical information about the correlation of values among the columns