前提html
本文僅討論SQL Server查詢時,
對於非複合統計信息,也即每一個字段的統計信息只包含當前列的數據分佈的狀況下,
在用多個字段進行組合查詢的時候,如何根據統計信息去預估行數的。
利用不一樣字段的統計信息作數據行數預估的算法原理,以及SQL Server 2012和SQL Server 2014該算法的差別狀況,
這裏暫時不涉及複合統計信息,暫不涉及統計信息的更新策略及優化相關話題,以及其餘SQL Server版本計算方式。
算法
統計信息是什麼數據庫
簡單說就是對某些字段的數據分佈的一種描述,讓SQL Server在根據條件作查詢的時候,大概知道預期的數據大小,
從而指導生成合理執行計劃的一種數據庫對象ide
統計信息的分類函數
索引上會自動建立統計信息,SQL Server也會根據具體的查詢,在某些非索引自動建立索引,固然也能夠經過手動方式建立統計信息。
先來直觀地瞭解一下統計信息長什麼樣,參考截圖,就是這麼個樣子,
_WA_Sys_****開頭的是系統根據須要建立的統計信息,
與索引同名的是索引上建立的統計信息,
手動建立統計信息也能夠在知足SQL Server命名要求的狀況下自行命名。oop
下面一個是索引的統計信息。性能
統計信息的做用測試
查詢引擎根據統計信息提供的數據作出合理的執行計劃。
那麼,查詢引擎到底是怎麼利用統計信息作預估的呢,
以及下面將要提到的SQL Server 2014中較以前的版本有哪些變化?
本文將對此兩點作一個簡單的分析來講明SQL Server是怎麼根據統計信息作估算的,下面開始正文。優化
測試環境搭建spa
習慣性地作一個演示的環境,建立一個表,寫入100W的數據後面測試用。
create table TestStatistics ( Id int identity(1,1), Status1 int, Status2 int, Status3 int )
insert into TestStatistics values (RAND()*1000,RAND()*250,RAND()*50) go 1000000
表中有四個字段,第一個是自增列,主要看Status1,Status2,Status3這三個字段,
三個字段的取值都是用隨機數乘以一個常量係數的出來的,
所以這三個字段的數據分佈範圍分別是
Status1:0-999(1000種數據分佈)
Status2:0-249(250種數據分佈)
Status3:0-49(50種數據分佈)
這個後面有用。
首先在SQL Server 2012中作測試
先作這麼一個查詢:select * from TestStatistics where Status1=885 and Status2=88 and Status3=8
這個查詢完成以後,表上自動建立一個三個統計信息,
這三個統計信息分別是Status1,Status2,Status3這個三個字段的數據分佈描述
首先來看一下其中這個_WA_Sys_00000002_0EA330E9,也即Status1這個列的統計信息的詳細信息,
注意All density字段值,選擇性是反應一個表中該字段的重複數據有多少或者說惟一性有多少,
計算方法是:1/表中該字段非重複個數。
上面說了,這個Status1這個列的取值範圍是0-999,一共有1000中取值可能行,
那麼這個選擇行就是1/1000=0.001,因此也是吻合這裏的All density=0.001的
照這麼計算,其他兩個字段的選擇度分別是1/250=0.004 和1/50=0.02,分別以下截圖的 All density。
執行計劃對數據行的預估
說完統計信息的基礎問題以後,咱們就能夠來觀察執行計劃對目標數據的預估規律了。
咱們來看這麼一個查詢,以下,注意這個是查詢的條件是參數變量,而不是直接的值,後面我會解釋爲何這麼作。
來觀察執行計劃對數據行的預估:能夠看出來,預估爲4行。
那麼這個4行是怎麼計算出來的呢?
這就要利用到咱們上面的選擇性了,
Status1字段的選擇性是0.001,Status2的選擇性是0.04,
在SQL Server 2012中,對數據行的預估計算方式是各個字段的選擇性的乘積,
假如Pn表明不一樣字段的選擇性,那麼預估行數的計算方法就是: 預估行數=p0*p1*p2*p3……*RowCount
所以,執行計劃顯示的:預估行數=0.001*0.004*總行數(也即1000000)= 4
說到這裏解釋兩個可能存在的幾個疑問:
第一,上述示例是用兩個字段查詢的,爲何不拿三個字段作演示說明?
首選,不論是多少個字段查詢,預估行數符合上述計算方式是沒有問題的,
可是若是經過上述公式計算出來的結果很是小,在少於1的狀況下,SQL Server顯示預估爲1行。
按照上述計算方法,用三個字段作查詢,
預估行數=0.001*0.004*0.02*總行數(也即1000000)= 0.08<1,因此預估爲1行。
第二,爲何不直接用值查詢,而是用變量作查詢?
熟悉SQL Server的同窗應該都知道,直接用變量查詢的時候,SQL Server編譯的時候不知道具體的參數值,
在不知道具體參數值的狀況下,它是使用字段的選擇性的時候是用到通常性(或者說是平均)的值,
也就是統計信息中總體計算出來字段的選擇性,也即All density=0.001
這裏暫定認爲數據分佈是均勻的,也即每一個值分佈差異不大。
但事實上每一個值的分佈的差異還有存在的,
尤爲是分佈不均勻的時候,固然這個是另一個很是大的話題了,這裏暫不討論。
若是直接用明確的值作查詢。
好比 select * from TestStatistic where Status1=885 and Status2=88
SQL Server會根據統計信息中每一個字段 :Status1=885 的行數和 Status2=88行數的具體的值,
利用上述公式作預估
那麼就繼續用具體的值作演示說明,
能夠直接用where Status1=885 and Status2=88這個條件查詢來觀察預估結果。
首先咱們看統計信息中Status1=885 的分佈行數,1079行
而後再看統計信息中Status2=88 的分佈行數,3996行
利用上述公式,預估行數爲4.31168行
那麼直接利用值作查詢是否是這個預估的行數呢?直接上圖,完美地吻合了上述的計算方法獲得的結果。
第三,沒有索引的狀況下是符合預估的計算方法,若是建立了索引呢?
查詢條件中的各個列的統計信息是非相關的,
若是分別在各個列上建立單個列的索引信息,在查詢的時候也屬於非相關統計信息。
如截圖,也就是說,雖然建立了索引,執行計劃發生了變化,
從一開始的表掃描變成了經過兩個索引查找後作hash join,而後Loop join查詢數據,咱無論它就是變成什麼執行計劃了
可是統對數據的預估仍是跟上面全表掃描同樣的,都是預估爲4.31168,沒有由於建立了索引以及執行計劃發生了變化而改變(預估行數)。
由於即使是建立了單列上的索引,執行計劃變了,可是統計信息仍是非相關的,也就是一個統計信息只描述一列字段的分佈狀況。
而後在SQL Server 2014中作測試
上述一樣的數據,我這裏經過link server 將上述SQL Server 2012實例下的測試表的結果導入到SQL Server 2014的實例下的表中。
如今表結構和數據徹底一致。
首選,作一個一樣的測試,利用兩個變量查詢的查詢條件作查詢,看看SQL Server 2014預估的算法有什麼變化。
還記得上面在 SQL Server 2012中一樣的寫法,一樣的數據的預估的狀況吧,剛纔預估的是4行,如今怎麼變成63.2456行了?
預估行數的計算公式變了嗎,固然變了,這正是本文要說的重點。
那麼SQL Server 2014中是怎麼預估的呢?公式是這麼來的:預估行數 = P0*P11/2 * P21/4 * P31/8……* RowCount
那麼來根據此計算方式來計算預估行數的問題:預估行數=0.001*0.0041/2*1000000 = ?
這裏我就不作開方運算了,拿來主義,直接用SQL Server來算拉倒了,SQL Server給咱們提供了一個開方函數(SQRT),真JB好用。
計算一下結果吧,
沒錯,是63.24555,保留四位有效數字的話就是63.2456了,預估行數跟上面計算出來的結果也是徹底吻合的。
補充測試1:
一樣地,用三個條件作查詢,預估算法也一樣複合上述公式的結果。
按照公式來計算預估行數,選擇性按照總體計算出來的選擇性來,一樣也是吻合的。
補充測試2:
若是把查詢條件換作具體的值,跟在SQL Server 2012中同樣,SQL Server2014 也一樣會根據具體的值得數據作計算
進行這麼個查詢:select * from TestStatistics2014 where Status1=858 and Status2=88
解釋一下爲何此次Status1換成858了:
由於即使表結構,數據徹底一致吧,受限於統計信息的步長(Steps)只有200,兩個庫的統計信息也不徹底一致,統計信息不能精確到任何一個值,
咱們這裏爲了演示這個算法,找一個具體的RANGE_HI_KEY值,比較容易說明問題。
首先看Status1=858的數據分佈狀況
再看Status2=88的數據分佈狀況
利用上述計算方法計算出來的預估:63.27713
執行計劃的預估:63.27713,也是徹底吻合的。
補充測試3,在查詢列上建立建立單獨的索引
跟SQL Server 2012中同樣,執行計劃發生了變化 ,可是對於數據行的預估,一樣並無由於執行計劃的變化而(預估行數)變化。
雖然執行計劃變了,可是對數據的預估並無變化,預估的算法仍是符合:預估行數 = P0*P11/2 * P21/4 * P31/8……* RowCount
在此能夠看出,執行計劃對於(未超過統計信息範圍的狀況下)數據行的預估,是有必定規律的,
這個規律就是:
SQL Server 2012 中,預估行數=p0*p1*p2*p3……*RowCount(Pn爲查詢字段的選擇性),
SQL Server 2014 中,預估行數= P0*P11/2 * P21/4 * P31/8……* RowCount(Pn爲查詢字段的選擇性)。
固然若是說統計信息過時或者取樣密度不夠,那就另當別論了,這個就關係到統計信息的更新策略問題了,也是一個很是大並且很是現實的問題,暫不深刻展開討論。
因此一開始我說暫不考慮統計信息自身是否理想,這裏是在統計信息很是完整的狀況下作測試的。
微軟爲何在SQL Server 2014中,對非相關且未超出統計信息範圍的預估行數算法作這麼一個變化,
由於PN的值是小於1的
預估行數的計算方法從p0*p1*p2*p3……*RowCount變化爲P0*P11/2 * P21/4 * P31/8……* RowCount,顯然是增長了預估行數的大小,
同時本文未說起的另一種狀況:對於超出統計信息範圍的狀況下,新的預估方法也增長預估行數的大小,
從總體上看,算法是傾向於"估多不估少」的,有這麼一個改變
至於爲何要作出這個改變?
若是常常作SQL優化的就會發現,很多問題都是少估了預期的數據行數(由於種種緣由吧,這裏暫時不討論爲何少估),
形成執行SQL時分配的資源不夠,從而拖慢了SQL的執行效率
一個很是典型的問題就是,預估的數據比實際的數據行數小,形成好比內存授予的不夠大,以及實際運算過程當中採用不合理的執行計劃
我的認爲,(控制在必定範圍以內的)估多的狀況下能夠經過獲取更多的系統資源來提高SQL的執行效率,
正常狀況下也不會說是跟實際值差的太離譜形成資源的浪費。
固然也有特殊狀況,那就另當別論
要注意的是我這裏有個前提,非相關的統計信息,不論是沒有任何索引,仍是是建立和單列上的索引,對應的統計信息,都屬於非相關統計信息,
若是建立複合索引(有人習慣叫組合索引),那麼執行計劃對於數據行的預估並不符合上述算法,具體算法我也不清楚。
此種狀況下,在SQL Server 2012和SQL Server 2014中預估算法也不同,這個有機會再研究吧。
對於測試結果的補充說明:
測試過程當中必定要保證統計信息的完整性,以及取樣的百分比問題,理性狀況下都是按照100%取樣的,
中間我略去了一些細節問題,好比沒此測試以前都會 update statistics TestStatistic with fullscan,保證100%取樣。
既然要精確到小數點後幾位,固然要求條件是理想狀況下的,目的就是必定要排除其餘條件對測試結果的影響。
總結:
本文經過一個簡單的示例,來了解了SQL Server經過統計信息對數據預估的計算方式和原理,以及SQL Server 2012和SQL Server2014之間的差別。
統計信息對於SQL執行計劃的選擇起着中樞神經般的做用,不光是在SQL Server數據庫中,包括其餘關係數據庫,統計信息都是一個很是重要的數據庫對象。
能夠說,SQL優化,統計信息以及與之息息相關的執行計劃是一個很是重要的因素,瞭解統計信息方面的知識對性能調優有着很是重要的做用。
在涉及到組合索引上的統計信息狀況下,執行計劃對數據行的預估,SQL Server2012和SQL Server 2014中也不同,問題將會更加有趣,待有時間再寫吧。
參考:Fanr_Zh 大神的 http://www.cnblogs.com/Amaranthus/p/3678647.html