在SQL Server中,我們要看懂執行計劃和統計信息,我們可能需要深刻理解一些關鍵詞,例如密度(Density)、選擇性(Selectivity)、謂詞(predicate)、基數(Cardinality)。前陣子,對密度和選擇性的概念模糊了,剛好看了Query Tuning Fundamenta... ...
在SQL Server中,我們要看懂執行計劃和統計信息,我們可能需要深刻理解一些關鍵詞,例如密度(Density)、選擇性(Selectivity)、謂詞(predicate)、基數(Cardinality)。前陣子,對密度和選擇性的概念模糊了,剛好看了Query Tuning Fundamentals: Density, Predicates, Selectivity, and Cardinality這篇文章, 遂結合自己的理解、以及相關案例、分析總結一下這些專業名稱。
謂詞(predicate)
什麼是謂詞呢?謂詞是取值為 TRUE、FALSE 或 UNKNOWN 的表達式。 謂詞用於WHERE子句和HAVING子句的搜索條件中,還用於FROM子句的聯接條件以及需要布爾值的其他構造中。官方的解釋為:A predicate is an expression that evaluates to True or False 。在WHERE條裡面的常見的謂詞形式有:
1: LIKE模糊查詢。
2: BETWEEN範圍查詢
3: IS NULL、IS NOT NULL判斷
4: IN - OR
5: EXIST
6: 等值查詢
..............................
我們先通過例子來看看一個謂詞(predicates)吧。如下所示, h.SalesOrderID > 43669 這個範圍查詢就是一個過濾謂詞。如下所示,在實際執行計劃中,右鍵單擊“Clustered Index Seek"查看細節。就會看到Seek Predicates。
USE AdventureWorks2014
GO
SELECT h.*
FROM Sales.SalesOrderHeader h
WHERE h.SalesOrderID > 43669;
SQL Server中有兩種謂詞:過濾謂詞和連接謂詞 ,還有所謂的SARG謂詞和非SARG謂詞概念。如上所示,上面的謂詞就屬於過濾謂詞,而位於LEFT/INNER/RIGHT JOIN的ON後面的為連接謂詞。 另外在SQL Server中還有隱式謂詞(implied predicates)的概念。使用跟蹤標記2324可以禁用隱式謂詞。 這裡對這些概念不做展開介紹。
密度(Density)
密度(Density)這個指標是用來衡量一個(或一組)列中,有多少唯一值。 它是一個比率值。 實際應用中值越小越好。不過,首先我們要區分DBCC SHOW_STATISTICS輸出的頭部信息(STAT_HEADER)中的這個Density指標和DENSITY_VECTOR中的Density指標。這兩者是有所區別的,其實一般我們所說的密度(Density)指DENSITY_VECTOR中密度,而不是STAT_HEADER中的Density。
在DBCC SHOW_STATISTICS輸出的頭部信息(STAT_HEADER),這個Density指標,官方文檔的介紹如下,具體參考DBCC SHOW_STATISTICS (Transact-SQL)鏈接:
Density:密度計算公式為 1/統計信息對象第一個鍵列中的所有值(不包括直方圖邊界值)的非重覆值。 查詢優化器不使用此 Density 值,顯示此值的目的是為了與 SQL Server 2008 之前的版本實現向後相容
Calculated as 1 / distinct values for all values in the first key column of the statistics object, excluding the histogram boundary values.
This Density value is not used by the query optimizer and is displayed for backward compatibility with versions before SQL Server 2008.
但是這裡發現頭部信息(STAT_HEADER)中Density的值計算並不像官方文檔介紹的那樣(具體見上面所述,這也是我很困惑的地方,個人猜測是文檔有錯誤,一直沒人更正,畢竟官方文檔也不能保證100%的準確性):
STAT_HEADER的Density的的計算公式為 ~= count(disitnct column_name)/count(*)
0.607627522644 ~= 0.6162394
註意:上面只能是約等於,不是等於關係。後面找了很多資料,發現其實(STAT_HEADER)中的這個Density指標的計算公式是這樣:
density =
(select distinct (column_name)
from table_name
where column_name not in (histogram range_hi_key values))
/ (select count(column_name)
from table_name
where column_name not in (histogram range_hi_key values))
具體到這個例子來說(對於複合索引,這個欄位是符合索引第一個欄位),如下所示:
SELECT COUNT(DISTINCT CustomerID)*1.0/COUNT(*)
FROM Sales.SalesOrderHeader
WHERE CustomerID NOT IN ( 11000, 11019, 11091, 11142, 11185, 11223, 11262,
11300, 11331, 11417, 11439, 11498, 11519, 11566,
11631, 11677, 11711, 11769, 11892, 11935, 12008,
12054, 12127, 12196, 12291, 12321, 12363, 12489,
12559, 12616, 12760, 12880, 12969, 13038, 13096,
13175, 13231, 13270, 13474, 13575, 13608, 13652,
13756, 13823, 13944, 13988, 14096, 14162, 14265,
14341, 14612, 14860, 14943, 15048, 15114, 15177,
15521, 15625, 15687, 15932, 15974, 16237, 16513,
16583, 16641, 16758, 16855, 16959, 17026, 17103,
17181, 17260, 17335, 17551, 17619, 17715, 17788,
17832, 17930, 18047, 18125, 18223, 18294, 18390,
18452, 18620, 18712, 18749, 19031, 19289, 19339,
19420, 19499, 19585, 20051, 20159, 20245, 20576,
20779, 20862, 20960, 21046, 21248, 21470, 21574,
21807, 21916, 22122, 22344, 22826, 23136, 23267,
23578, 23725, 24159, 24257, 24466, 24754, 24887,
25114, 25400, 25555, 25819, 25916, 25995, 26127,
26276, 26564, 26686, 26841, 27197, 27361, 27672,
28050, 28389, 28749, 28919, 29105, 29270, 29448,
29508, 29603, 29669, 29698, 29723, 29795, 29857,
&n