對於同一句話,SQL SERVER 有不少種方法來完成它。有些方法適合於數據量比較小的時候,有些方法適合於數據量比較大的時候。同一種方法,在數據量不一樣的時候,複雜度會有很是大的差異。索引只能幫助SQL SERVER找到符合條件的記錄。SQL SERVRE 還須要知道每一種操做所要處理的數據量有多少,從而估算出複雜度,選取一個代價最小的執行計劃。說得通俗一點,SQL SERVR要可以知道數據是「長得什麼樣」的,才能用最快的方法完成指令。怎麼可以讓SQL SERVER知道數據的分佈信息呢?在數據庫管理系統裏有個經常使用的技術,就是數據的「統計信息」(STATISTICS)。SQL SERVER 是經過它瞭解數據的分佈狀況的。算法
咱們能夠看一下上次2張表在SalesOrderID這個字段上的統計信息,以便對於直觀認識。sql
先手動更新一下:SalesOrderHeader_TEST 這個表的統計信息,在統計信息的維護上面說明爲何這裏要手機更新:數據庫
UPDATE STATISTICS dbo.SalesOrderHeader_TEST GO -- 再運用腳本: DBCC SHOW_STATISTICS(SalesOrderHeader_TEST , SalesOrderHeader_TEST_CL) GO
結果以下圖:less
保存的是每一個訂單的概要信息,一張訂單隻會有一條記錄,因此SalesOrderID是不會重複的。如今這張表裏,應該有31474 條記錄,SalesOrderID是一個int型的字段,因此字段長度是4。運用DBCC SHOW_STATISTICS(<table_name> , <index_name>) 命令能夠獲得統計信息內容。性能
統計信息內容分3部分spa
列名設計 |
說明code |
|
Nameserver |
統計信息名稱。這裏就是索引的名字: SalesOrderHeader_test_CL對象 |
|
Updated |
上一次更新統計信息的日期和時間。這裏是09 6 2013 4:22PM這個時間很是重要,根據它可以判斷統計信息是何時更新的,是否是數據量發生變化之後,是否是存在統計信息不能反映當前數據分佈特色的問題。 |
|
Rows |
表中的行數。這裏是31474行,徹底正確反映了當前表的數據量 |
|
Rows Sampled |
統計信息的抽樣行數。這裏也是31474,說明上次SQL SERVER更新統計信息的時候,對整個表裏全部記錄的SalesOrderID字段,都掃了一遍,這樣作出來的統計信息通常都都是很精確的。 |
|
Steps |
在統計信息的第3部分,會把數據分紅幾組。這裏是3組 |
|
Density |
第一個列前綴的選擇性(不包括 EQ_ROWS) |
|
Average key length |
若是列的平均長度,由於SalesOrderHeader_test_cl索引只有一列,數據類型是int ,因此長度就是4 |
|
String Index |
若是爲「是」,則統計信息中包含字符串摘要索引,以支持爲like條件估算結果算大小、nvarchar(max)、text 以及 ntext 數據類型的關導列,這裏是int ,因此這個是爲」no」 |
2. 數據字段的選擇性
列名 |
說明 |
|
All density |
反映索引列的選擇性(select ivity)「選擇性」反映數據集裏重複的數據量多少,或者反過來講,值惟一的數據量有多少,若是一個字段的靈氣不多有重複,那麼它的可選擇性就比較高。好比身份證號,是不可重複的。哪怕對整個中國的身份記錄作調查,代入一個身份證號碼,最多隻會有一條記錄返回。在這樣字段上的過濾條件,可以有效地過濾掉大量數據,返回的結果集會比較小。舉一個相反的例子,性別。非男即女,這個字段上的重複性就很高,選擇性就很低,一個過濾條件,最多隻能過濾掉一半的記錄。 SQL SERVER經過計算「選擇性」,使得本身可以預測一個過濾條件作完後,大概能有多少記錄返回。 Density的定義是:desity = 1/Cardinality of index keys 若是這個值小於0.1,通常講這個索引的選擇性比較高。若是大於0.1,它的選擇就不高了。
|
|
Average length |
索引列的平均長度,這裏仍是4 |
|
Columns |
索引列的名稱, 這裏是字段 SalesOrderID |
從這一部分的信息,能夠推斷出統計所關心的字段的長度,以及它有多少條惟一值。可是這些信息對SQL SERVER預測結果集複雜度還不夠。好比我如今要查一個SalesOrderID = 60000的訂單,仍是不知道會有多少記錄返回。這裏須要第3部分信息
3. 直方圖(histogram)
列名 |
說明 |
|
Range_hi_key |
直方圖裏第一組(step)數據的最大值。訂單號的最小號碼在表裏是43659。這裏是sql server選擇它做爲第一個step的最大值。3組數據分別是:.. ~~43659 , 43660 ~~75131 ,75132~~75132 |
|
Range_rows |
每組數據區間行數據,上限值除外。第一組只有一個數:43659,第三組也只有一個數: 75132. 其它數據都在第二個組裏,區別裏有31471個數。 |
|
Eq_Rows |
表中與直方圖每組數據上限值相等的行數目。 這裏都是1 |
|
District_range_rows |
走方圖裏每組數據區間非重複值的數目,上限值除外。因爲這個字段沒有重複的值,因此這裏distinct_reange_rows的值就等於range_rows的值 |
|
Avg_range_rows |
直方圖裏每組數據區間內重複值的平均數目,上限值除外。 計算公式 = (Range_rows/district_range_Rows for distinct_range_rows>0)這裏district_range_Rows的值就等於 Range_rows的值,因此avg_range_rows = 1 |
|
有這麼一個直方圖,就可以很好地知道表格裏的數據分佈了。在SalesOrderID這個字段裏,最小值是43659,最大75132,在這個區間裏有31473個值,且沒有重複值,因此要吧推算出表格裏的值就是從43658 到 75132結束的每一個int值、 SQ L SERVER 沒有必要存儲不少step的信息,只要這3個step,就可以徹底表達數據分佈。假設查詢條件是在43659 – 75132之間的值,那麼SQL SERVER知道會返回一行。若是不在這個區別,就不會有行返回。而返回的每一行長度,都是4. 經過這些統計信息,SQL SERVER可以比較好地預測返回的結果集的行數和長度。
注:1. 若是一個統計信息是爲一組字段創建的,例如,一個複合索引創建在兩個以上的字段上,SQL SERVER維護全部字段的選擇性信息,可是隻會維護第一個字段的直方圖。
2. 當表格比較大的時候,SQL SERVER 在更新統計信息的時候爲了下降消耗,只會取表格的一部分數據作抽樣(Rows Sampled),這時候統計信息裏面的數據都是根據這些抽樣數據估算出來的值,可能和真實值會有些差別。
統計信息越細,就越準確,可是維護統計信息要付出的額外開銷也會很大,有可能提升統計信息精確度所帶來的執行性能的提高,還抵不了維護統計信息成本的增長。SQL SERVER作這樣的設計,不是由於其能力有限,而是爲了謀求一個對大多數狀況都合適的平衡。
剛纔看的索引 SalesOrderHeader_test_CL數據分佈比較簡單。下面來講一個稍微複雜一點的索引。SalesOrderDetail_TEST_NCL
SalesOrderDetail_TEST這張表造數據的時候,作的比較特別,它的前10%的數據,屬於編號43659 – 75132這3萬多條訂單,然後90%的數據,平均屬於43659 – 75132這9張訂單。來看一下統計信息是如何表示的。
DBCC SHOW_STATISTICS(SalesOrderDetail_test, SalesOrderDetail_TEST_NCL)
這個統計信息和SalesOrderHeader_test_CL有不少不一樣
一、 這裏的數據分組(step)/有190個,要詳細不少。
二、 在第2部分Density,不但有索引列(SalessOrderID)的選擇值,還有SalesOrderID + SalesOrderDetailID 合併起來的選擇值。能夠看出若是同時使用2個字段進行過濾,其選擇性8.242868E-07 會比只使用 SalesOrderID (3.177226E-05) 還要高
三、 走廊圖只有SalesOrderID的信息,沒有 SalesOrderDetailID 的信息。從直方圖的各項值分佈狀況,能夠清楚地看出 SalesOrderHeader_test 這張表的數據分佈特色。SQL SERVER可以根據供稿的 SalesOrderID值,推斷出是隻有幾條、幾十條記錄返回(當SalesOrderID 在43659到75123之間),仍是會有12萬條數據返回(當SalesOrderID 在75124到75132之間)。
下面兩段代碼雖然結構如出一轍,可是由於參數值不一樣,SQL SERVER選擇了不一樣的執行計劃,下圖,這是由於SQL SERVER知道一個只會返回3行(EstimateRows), 然後一個會返回 121317行,這裏SQL SERVER 猜得是徹底正確的。
SET STATISTICS PROFILE ON SELECT B.SalesOrderID , B.OrderDate , A.* FROM dbo.SalesOrderDetail_TEST A INNER JOIN dbo.SalesOrderHeader_TEST B ON A.SalesOrderID = B.SalesOrderID WHERE B.SalesOrderID = 72642 SET STATISTICS PROFILE OFF
SET STATISTICS PROFILE ON SELECT B.SalesOrderID , B.OrderDate , A.* FROM dbo.SalesOrderDetail_TEST A INNER JOIN dbo.SalesOrderHeader_TEST B ON A.SalesOrderID = B.SalesOrderID WHERE B.SalesOrderID = 75127 SET STATISTICS PROFILE OFF
在SQL SERVER數據庫屬性裏,有兩個默認打開的設置AUTO_CREATE_STATISTICS 和 AUTO_UPDATE_STATISTICS 他們可以讓SQL SERVER在須要的時候,自動創建要用到的統計信息,也能在發現統計信息過期的時候,自動去更新它。 何時會建立統計信息呢?主要有3種狀況:
一、 在索引建立時,SQL SERVER會自動地在索引所在列上建立統計信息。
因此從某種角度講,索引的做用是雙重的。它本身可以幫助SQL SERVRE快速找到數據。而它上面的統計信息,也可以告訴SQL SERVER 數據的分佈狀況
二、 管理員也能夠經過 CREATE STATISTICS 之類的語句手動建立他認爲須要的統計信息
若是打開 AUTO_CREATE_STATISTICS,通常來說不多須要手動建立。
三、 當SQL SERVER想要使用某些列上的統計信息,發現沒有的時候, "auto create statistics" 會讓 sql server自動建立統計信息
例如: 當語句要在某個(或者某幾個)字段上作過濾,或者要拿它(們)和另一張表聯接(JOIN), SQL SERVER 要估算最後從這個表會返回多少條記錄。這個時間就須要一個統計信息的支持。若是沒有,SQL SERVER會自動建立一個。
USE AdventureWorks2008 GO -- 返回指定表中列和索引的統計信息。(索引上的除外) sp_helpstats @objname = 'dbo.SalesOrderHeader_TEST' go -- 結果顯示:此對象沒有任何統計信息。 SELECT COUNT(*) FROM dbo.SalesOrderHeader_TEST where OrderDate = '2004-06-11 00:00:00.000' go sp_helpstats @objname = 'dbo.SalesOrderHeader_TEST' go -- 顯示結果 /* statistics_name statistics_keys _WA_Sys_00000003_7F80E8EA OrderDate */
由上面的例子能夠看出,在打開 AUTO_CREATE_STATISTICS 的數據庫上,不用擔憂 SQL SERVER沒有足夠的統計信息來選擇執行計劃。
SQL SERVER不只要創建合適的統計信息,還要及時更新它們,使他們可以反映表裏數據的變化。數據的插入、刪除、修改均可能會引發統計信息的更新。可是,更新統計信息自己是一件消耗資源的事情,尤爲是對比較大的表。若是有一點點小的修改SQL SERVER都要去更新統計信息,可能 SQL SERVER就光忙活這個,來不及作其它事了。 SQL SERVER 仍是要在統計信息的準確度和資源合理消耗之間作一個平衡。在SQL SERVER 2005/08,觸發統計信息自動更新的條件是:
一、若是統計信息是定義在變通表格上的,那麼當發生下面變化之一後,統計信息就被認爲是過期的,下次使用時,會自動觸發一個更新動做。
(1)表格從沒有數據變成有大於等於1條數據
(2)對於數據量小於500行的表格,當統計信息的第一個字段數據累計變化大於500之後。
(3)對於數據量大於500行的表格,當統計信息的第一個字段數據累計變化量大於500 + (20%)
二、臨時表(temp table) 上能夠有統計信息。其維護策略基本和普通表格一致。可是表變量(table variable) 上不能建統計信息。
這樣的維護策略可以保證花費比較小的代價,確保統計信息基本正確。後面會有安例,反映這個維護策略在數據分佈特殊的表上,也有可能形成一些負面的影響。
在SQL SERVER2005之後,數據庫屬性多了一個‘Auto Update Statistics Asynchronously’。當SQL SERVER發現某個統計信息過期時,它會用老的統計信息繼續如今的查詢編譯,可是會在後臺啓動一個任何,更新這個統計信息。這樣下一次統計信息被使用時,就已是一個更新過的版本。這樣作的缺點,是不能保證當前這句查詢的執行計劃準確性。凡事有利有弊,數據庫管理員能夠根據實際狀況作選擇。 固然,的確有一些例外狀況。因爲數據特殊性,會使得 SQL SERVER 這種 auto update statistics 的算法不能知足確保執行計劃確實性的需求。在實際使用中,有時候數據庫的性能會忽然之間慢下來。有經驗的管理員會安排一次索引重建的任務,經常對性能會有所幫助。一般人們會解釋爲,由於索引重建消除了數據碎片,於是提升了性能。其實索引重建還作了另一件很重要的工做,它使用full scan 的方式,從新更新了表上的統計信息,使得統計信息很是精確。這對性能幫助做用也會很大。