在日常生活中,我們用統計信息來采取決定。SQL Server優化器也用同樣的方式,使用統計信息來選擇正確的執行計劃。如果統計信息錯誤或過期,SQL Server可能就會選擇錯誤的執行計劃。在這個文章里,我們換個方式理解下統計信息。
查詢優化器使用統計信息來判斷每一步返回的行。執行計劃里的估計行數信息是基于列的可用統計信息計算而來的。統計信息給我們列里數據分布情況。沒有統計信息,查詢優化器不能夠判斷不同計劃的效率。通過使用統計信息,查詢優化器在訪問數據時可以做出正確的選擇。
在我們定義索引時,統計信息會自動創建。除此之外,當列在查詢里被引用,作為WHERE條件的一部分,在group by子句里或join條件里,統計信息都會創建。為了自動創建統計信息,在數據庫層里的AUTO_CREATE_STATISTICS設置應該被啟用。默認情況下這個設置是被啟用的。除此之外,統計信息可以使用CREATE STATISTICS命令創建。
在SQL Server里存儲的統計數字是關于密度向量和直方圖(數據分布)的信息。在我們討論更多細節前,先理解這2個概念。
密度向量:在給出列或一組列唯一值的比例。統計密度向量的公式:1/列(或一組列)不同值個數。
密度向量用來衡量列的唯一性或列的選擇性。密度向量的值在0和1之間。如果這列的密度值為1,表示這列的所有記錄值一樣,選擇性低。更高的密度帶來更低的選擇性。如果這列的密度值為0.003,表示這列有1/0.003=333個不同值。
我們來看個例子,用下列語句創建表并在上面建立2個索引。
1 USE StatisticsDB2 GO3 4 SELECT * INTO SalesOrderDetail FROM AdventureWorks2008R2.Sales.SalesOrderDetail5 CREATE UNIQUE CLUSTERED INDEX ix_SalesOrderDetailID ON SalesOrderDetail(SalesOrderDetailID)6 CREATE NONCLUSTERED INDEX ix_PRoductid ON SalesOrderDetail(productid)
我們來看看這2個索引的統計信息。
1 DBCC SHOW_STATISTICS('dbo.SalesOrderDetail', 'ix_SalesOrderDetailID')
輸出結果有3個表,分別是:統計信息頭,密度向量和直方圖。
在第1部分(統計信息頭)
可以使用DBCC SHOW_STATISTICS加WITH STAT_HEADER來只獲取統計頭信息。
在第2部分的密度表,我們只看到一條記錄,因為在我們索引里只有1列。
所有密度列給我們SalesOrderDetailsId列的密度值(1/列(或一組列)不同值個數)。所有密度列給我們值 8.242868E-06 =0.000008242868。這表示SalesOrderDetailsId列有 1/0.000008242868=121317個唯一值,我們可以用下列語句驗證下。
1 SELECT COUNT(DISTINCT SalesOrderDetailID ) FROM SalesOrderDetail
可以使用DBCC SHOW_STATISTICS加WITH DENSITY_VECTOR來只獲取密度向量信息。
我們來看看非聚集索引ix_productid的密度。
1 DBCC SHOW_STATISTICS('dbo.SalesOrderDetail', 'ix_productid') WITH DENSITY_VECTOR
在密度表里可以看到2行記錄,即使我們的非聚集索引是在單列上。這是因為聚集鍵是非聚集索引的一部分(參見索引深入淺出(4/10):非聚集索引的B樹結構在聚集表)。
第一行告訴我們,ProductID列的密度向量值為0.003759399,換句話說,在ProductID列有1/0.003759399=226個唯一值。我們可以驗證下。
1 SELECT COUNT(DISTINCT ProductID) FROM dbo.SalesOrderDetail
第二行告訴我們,ProductID和SalesOrderDetailID組合列的密度向量值是0.000008242868,換句話說,ProductID和SalesOrderDetailID組合列有121317個唯一值,這個和表的總記錄數是一致的。
密度向量值會用在哪里呢,我們看下下面查詢的執行計劃,點擊工具欄的顯示包含實際的執行計劃。
1 SELECT ProductID,COUNT(*) FROM dbo.SalesOrderDetail GROUP BY ProductID
在執行計劃里,在流聚合運算符的輸出里,我們看到估計行數是266。在ProductID列唯一值(個數)可以從密度表里拿到。但我們怎么證明查詢優化器是用這個值來計算估計行數。我們創建另外一個沒有任何索引的SalesOrderDetail表。
1 SELECT * INTO SalesOrderDetail_NoStats FROM SalesOrderDetail
通過下面語句我們可以看出,這表沒有任何的統計信息。
1 EXEC SP_HELPSTATS 'SalesOrderDetail_NoStats', 'ALL'
我們再看下這個表的剛才查詢,點擊工具欄的顯示包含實際的執行計劃:
1 SELECT ProductID,COUNT(*) FROM dbo.SalesOrderDetail_NoStats GROUP BY ProductID
在沒有任何統計索引和統計信息的情況下,優化器再一次在流聚合運算符的輸出里估計行數是266。我們再次檢查下這個表的統計信息。
1 EXEC SP_HELPSTATS 'SalesOrderDetail_NoStats', 'ALL'
是的,在估計執行計劃是,SQL Server在productID列創建了統計信息來幫助優化器選擇正確的執行計劃。我們來看看這個統計信息的詳情。
1 DBCC SHOW_STATISTICS('dbo.SalesOrderDetail_NoStats', '_WA_Sys_00000005_7E6CC920')
在統計信息頭,我們注意到Rows Sampled值比Rows值小。這是因為在自動創建統計信息時,SQL Server沒有掃描整個表,只掃描了表的樣本。為什么會這樣在接下來的文章里會談到。簡而言之,在非聚集索引字段里,統計信息幫助優化器在每一步操作時判斷估計行數,什么樣的連接是合適的,還有在計劃中的執行順序。
新聞熱點
疑難解答