1. 程式人生 > >看懂SqlServer查詢計劃

看懂SqlServer查詢計劃

對於SQL Server的優化來說,優化查詢可能是很常見的事情。由於資料庫的優化,本身也是一個涉及面比較的廣的話題, 因此本文只談優化查詢時如何看懂SQL Server查詢計劃。畢竟我對SQL Server的認識有限,如有錯誤,也懇請您在發現後及時批評指正。

首先,開啟【SQL Server Management Studio】,輸入一個查詢語句看看SQL Server是如何顯示查詢計劃的吧。
說明:本文所演示的資料庫,是我為一個演示程式專用準備的資料庫, 可以在此網頁中下載。

select v.OrderID, v.CustomerID, v.CustomerName, v.OrderDate, 
v.SumMoney, v.Finished from OrdersView as v where v.OrderDate >= '2010-12-1' and v.OrderDate < '2011-12-1';

其中,OrdersView是一個檢視,其定義如下:

SELECT     dbo.Orders.OrderID, dbo.Orders.CustomerID, dbo.Orders.OrderDate, 
            dbo.Orders.SumMoney, dbo.Orders.Finished, 
            ISNULL(dbo.Customers.
CustomerName, N'') AS CustomerName FROM dbo.Orders LEFT OUTER JOIN dbo.Customers ON dbo.Orders.CustomerID = dbo.Customers.CustomerID

對於前一句查詢,SQL Server給出的查詢計劃如下(點選工具欄上的【顯示估計的執行計劃】按鈕):

從這個圖中,我們至少可以得到3個有用的資訊:
1. 哪些執行步驟花費的成本比較高。顯然,最右邊的二個步驟的成本是比較高的。
2. 哪些執行步驟產生的資料量比較多。對於每個步驟所產生的資料量, SQL Server的執行計劃是用【線條粗細】來表示的,因此也很容易地從分辨出來。
3. 每一步執行了什麼樣的動作。

對於一個比較慢的查詢來說,我們通常要知道哪些步驟的成本比較高,進而,可以嘗試一些改進的方法。 一般來說,如果您不能通過:提高硬體效能或者調整OS,SQL Server的設定之類的方式來解決問題,那麼剩下的可選方法通常也只有以下這些了:
1. 為【scan】這類操作增加相應欄位的索引。
2. 有時重建索引或許也是有效的,具體情形請參考後文。
3. 調整語句結構,引導SQL Server採用其它的查詢方案去執行。
4. 調整表結構(分表或者分割槽)。

下面再來說說一些很重要的理論知識,這些內容對於執行計劃的理解是很有幫助的。

回到頂部

SQL Server 查詢記錄的方法

說到這裡,不得不說SQL Server的索引了。SQL Server有二種索引:聚集索引和非聚集索引。二者的差別在於:【聚集索引】直接決定了記錄的存放位置, 或者說:根據聚集索引可以直接獲取到記錄。【非聚集索引】儲存了二個資訊:1.相應索引欄位的值,2.記錄對應聚集索引的位置(如果表沒有聚集索引則儲存記錄指標)。 因此,如果能通過【聚集索引】來查詢記錄,顯然也是最快的。

SQL Server 會有以下方法來查詢您需要的資料記錄:
1. 【Table Scan】:遍歷整個表,查詢所有匹配的記錄行。這個操作將會一行一行的檢查,當然,效率也是最差的。
2. 【Index Scan】:根據索引,從表中過濾出來一部分記錄,再查詢所有匹配的記錄行,顯然比第一種方式的查詢範圍要小,因此比【Table Scan】要快。
3. 【Index Seek】:根據索引,定位(獲取)記錄的存放位置,然後取得記錄,因此,比起前二種方式會更快。
4. 【Clustered Index Scan】:和【Table Scan】一樣。注意:不要以為這裡有個Index,就認為不一樣了。 其實它的意思是說:按聚集索引來逐行掃描每一行記錄,因為記錄就是按聚集索引來順序存放的。 而【Table Scan】只是說:要掃描的表沒有聚集索引而已,因此這二個操作本質上也是一樣的。
5. 【Clustered Index Seek】:直接根據聚集索引獲取記錄,最快!

所以,當發現某個查詢比較慢時,可以首先檢查哪些操作的成本比較高,再看看那些操作在查詢記錄時, 是不是【Table Scan】或者【Clustered Index Scan】,如果確實和這二種操作型別有關,則要考慮增加索引來解決了。 不過,增加索引後,也會影響資料表的修改動作,因為修改資料表時,要更新相應欄位的索引。所以索引過多,也會影響效能。 還有一種情況是不適合增加索引的:某個欄位用0或1表示的狀態。例如可能有絕大多數是1,那麼此時加索引根本就沒有意義。 這時只能考慮為0或者1這二種情況分開來儲存了,分表或者分割槽都是不錯的選擇。

如果不能通過增加索引和調整表來解決,那麼可以試試調整語句結構,引導SQL Server採用其它的查詢方案去執行。 這種方法要求: 1.對語句所要完成的功能很清楚, 2.對要查詢的資料表結構很清楚, 3.對相關的業務背景知識很清楚。 如果能通過這種方法去解決,當然也是很好的解決方法了。不過,有時SQL Server比較智慧,即使你調整語句結構,也不會影響它的執行計劃。

如何比較二個相同功能的SQL語句的效能好壞呢,我建議採用二種方法: 1. 直接把二個查詢語句放在【SQL Server Management Studio】,然後去看它們的【執行計劃】,SQL Server會以百分比的方式告訴你二個查詢的【查詢開銷】。 這種方法簡單,通常也是可以參考的,不過,有時也會不準,具體原因請接著往下看(可能索引統計資訊過舊)。
2. 根據真實的程式呼叫,寫相應的測試程式碼去呼叫:這種方法就麻煩一些,但是它更能代表現實呼叫情況, 得到的結果也是更具有參考價值的,因此也是值得的。

回到頂部

SQL Server Join 方式

在SQL Server中,每個join命令,都會在內部執行時採用三種更具體的方式來執行:

1. 【Nested Loops join】,如果一個聯接輸入很小,而另一個聯接輸入很大而且已在其聯接列上建立了索引, 則索引 Nested Loops 連線是最快的聯接操作,因為它們需要的 I/O 和比較都最少。

巢狀迴圈聯接也稱為“巢狀迭代”,它將一個聯接輸入用作外部輸入表(顯示為圖形執行計劃中的頂端輸入),將另一個聯接輸入用作內部(底端)輸入表。外部迴圈逐行處理外部輸入表。內部迴圈會針對每個外部行執行,在內部輸入表中搜索匹配行。可以用下面的偽碼來理解:

foreach(row r1 in outer table)
    foreach(row r2 in inner table)
        if( r1, r2 符合匹配條件 )
            output(r1, r2);

最簡單的情況是,搜尋時掃描整個表或索引;這稱為“單純巢狀迴圈聯接”。如果搜尋時使用索引,則稱為“索引巢狀迴圈聯接”。如果將索引生成為查詢計劃的一部分(並在查詢完成後立即將索引破壞),則稱為“臨時索引巢狀迴圈聯接”。查詢優化器考慮了所有這些不同情況。

如果外部輸入較小而內部輸入較大且預先建立了索引,則巢狀迴圈聯接尤其有效。在許多小事務中(如那些隻影響較小的一組行的事務),索引巢狀迴圈聯接優於合併聯接和雜湊聯接。但在大型查詢中,巢狀迴圈聯接通常不是最佳選擇。

2. 【Merge Join】,如果兩個聯接輸入並不小但已在二者聯接列上排序(例如,如果它們是通過掃描已排序的索引獲得的),則合併聯接是最快的聯接操作。如果兩個聯接輸入都很大,而且這兩個輸入的大小差不多,則預先排序的合併聯接提供的效能與雜湊聯接相近。但是,如果這兩個輸入的大小相差很大,則雜湊聯接操作通常快得多。

合併聯接要求兩個輸入都在合併列上排序,而合併列由聯接謂詞的等效 (ON) 子句定義。通常,查詢優化器掃描索引(如果在適當的一組列上存在索引),或在合併聯接的下面放一個排序運算子。在極少數情況下,雖然可能有多個等效子句,但只用其中一些可用的等效子句獲得合併列。

由於每個輸入都已排序,因此 Merge Join 運算子將從每個輸入獲取一行並將其進行比較。例如,對於內聯接操作,如果行相等則返回。如果行不相等,則廢棄值較小的行並從該輸入獲得另一行。這一過程將重複進行,直到處理完所有的行為止。

合併聯接操作可以是常規操作,也可以是多對多操作。多對多合併聯接使用臨時表儲存行(會影響效率)。如果每個輸入中有重複值,則在處理其中一個輸入中的每個重複項時,另一個輸入必須重繞到重複項的開始位置。 可以建立唯一索引告訴SQL Server不會有重複值。

如果存在駐留謂詞,則所有滿足合併謂詞的行都將對該駐留謂詞取值,而只返回那些滿足該駐留謂詞的行。

合併聯接本身的速度很快,但如果需要排序操作,選擇合併聯接就會非常費時。然而,如果資料量很大且能夠從現有 B 樹索引中獲得預排序的所需資料,則合併聯接通常是最快的可用聯接演算法。

3. 【Hash Join】,雜湊聯接可以有效處理未排序的大型非索引輸入。它們對複雜查詢的中間結果很有用,因為: 1. 中間結果未經索引(除非已經顯式儲存到磁碟上然後建立索引),而且通常不為查詢計劃中的下一個操作進行適當的排序。 2. 查詢優化器只估計中間結果的大小。由於對於複雜查詢,估計可能有很大的誤差,因此如果中間結果比預期的大得多,則處理中間結果的演算法不僅必須有效而且必須適度弱化。

雜湊聯接可以減少使用非規範化。非規範化一般通過減少聯接操作獲得更好的效能,儘管這樣做有冗餘之險(如不一致的更新)。雜湊聯接則減少使用非規範化的需要。雜湊聯接使垂直分割槽(用單獨的檔案或索引代表單個表中的幾組列)得以成為物理資料庫設計的可行選項。

雜湊聯接有兩種輸入:生成輸入和探測輸入。查詢優化器指派這些角色,使兩個輸入中較小的那個作為生成輸入。

雜湊聯接用於多種設定匹配操作:內部聯接;左外部聯接、右外部聯接和完全外部聯接;左半聯接和右半聯接;交集;聯合和差異。此外,雜湊聯接的某種變形可以進行重複刪除和分組,例如 SUM(salary) GROUP BY department。這些修改對生成和探測角色只使用一個輸入。

雜湊聯接又分為3個型別:記憶體中的雜湊聯接、Grace 雜湊聯接和遞迴雜湊聯接。

記憶體中的雜湊聯接:雜湊聯接先掃描或計算整個生成輸入,然後在記憶體中生成雜湊表。根據計算得出的雜湊鍵的雜湊值,將每行插入雜湊儲存桶。如果整個生成輸入小於可用記憶體,則可以將所有行都插入雜湊表中。生成階段之後是探測階段。一次一行地對整個探測輸入進行掃描或計算,併為每個探測行計算雜湊鍵的值,掃描相應的雜湊儲存桶並生成匹配項。

Grace 雜湊聯接:如果生成輸入大於記憶體,雜湊聯接將分為幾步進行。這稱為“Grace 雜湊聯接”。每一步都分為生成階段和探測階段。首先,消耗整個生成和探測輸入並將其分割槽(使用雜湊鍵上的雜湊函式)為多個檔案。對雜湊鍵使用雜湊函式可以保證任意兩個聯接記錄一定位於相同的檔案對中。因此,聯接兩個大輸入的任務簡化為相同任務的多個較小的例項。然後將雜湊聯接應用於每對分割槽檔案。

遞迴雜湊聯接:如果生成輸入非常大,以至於標準外部合併的輸入需要多個合併級別,則需要多個分割槽步驟和多個分割槽級別。如果只有某些分割槽較大,則只需對那些分割槽使用附加的分割槽步驟。為了使所有分割槽步驟儘可能快,將使用大的非同步 I/O 操作以便單個執行緒就能使多個磁碟驅動器繁忙工作。

在優化過程中不能始終確定使用哪種雜湊聯接。因此,SQL Server 開始時使用記憶體中的雜湊聯接,然後根據生成輸入的大小逐漸轉換到 Grace 雜湊聯接和遞迴雜湊聯接。 
如果優化器錯誤地預計兩個輸入中哪個較小並由此確定哪個作為生成輸入,生成角色和探測角色將動態反轉。雜湊聯接確保使用較小的溢位檔案作為生成輸入。這一技術稱為“角色反轉”。至少一個檔案溢位到磁碟後,雜湊聯接中才會發生角色反轉。

說明:您也可以顯式的指定聯接方式,SQL Server會盡量尊重您的選擇。比如你可以這樣寫:inner loop join, left outer merge join, inner hash join 
但是,我還是建議您不要這樣做,因為SQL Server的選擇基本上都是正確的,不信您可以試一下。

好了,說了一大堆理論東西,再來個實際的例子解釋一下吧。

回到頂部

更具體執行過程

前面,我給出一張圖片,它反映了SQL Server在執行某個查詢的執行計劃,但它反映的資訊可能不太細緻,當然,您可以把滑鼠指標移動某個節點上,會有以下資訊出現:

剛好,我裝的是中文版的,上面都是漢字,我也不多說了。我要說的是另一種方式的執行過程,比這個包含更多的執行資訊, 而且是實際的執行情況。(當然,您也可以繼續使用圖形方式,在執行查詢前點選工具欄上的【包括實際的執行計劃】按鈕)

讓我們再次回到【SQL Server Management Studio】,輸入以下語句,然後執行。

set statistics profile on 

select v.OrderID, v.CustomerID, v.CustomerName, v.OrderDate, v.SumMoney, v.Finished
from   OrdersView as v
where v.OrderDate >= '2010-12-1' and v.OrderDate < '2011-12-1';

注意:現在加了一句,【set statistics profile on 】,得到的結果如下:

可以從圖片上看到,執行查詢後,得到二個表格,上面的表格顯示了查詢的結果,下面的表格顯示了查詢的執行過程。相比本文的第一張圖片, 這張圖片可能在直觀上不太友好,但是,它能反映更多的資訊,而且尤其在比較複雜的查詢時,可能看起來更容易,因為對於複雜的查詢,【執行計劃】的步驟太多,圖形方式會造成圖形過大,不容易觀察。 而且這張執行過程表格能反映2個很有價值的資料(前二列)。

還是來看看這個【執行過程表格】吧。我來挑幾個重要的說一下。
【Rows】:表示在一個執行步驟中,所產生的記錄條數。(真實資料,非預期)
【Executes】:表示某個執行步驟被執行的次數。(真實資料,非預期)
【Stmt Text】:表示要執行的步驟的描述。
【EstimateRows】:表示要預期返回多少行資料。

在這個【執行過程表格】中,對於優化查詢來說,我認為前三列是比較重要的。對於前二列,我上面也解釋了,意思也很清楚。 前二列的數字也大致反映了那些步驟所花的成本,對於比較慢的查詢中,應該留意它們。 【Stmt Text】會告訴你每個步驟做了什麼事情。對於這種表格,它所要表達的其實是一種樹型資訊(一行就表示在圖形方式下的一個節點), 所以,我建議從最內層開始去讀它們。做為示例,我來解釋一下這張表格它所表達的執行過程。

第5行:【Clustered Index Seek(OBJECT:([MyNorthwind].[dbo].[Customers].[PK_Customers]), SEEK:([MyNorthwind].[dbo].[Customers].[CustomerID]=[MyNorthwind].[dbo].[Orders].[CustomerID]) ORDERED FORWARD)】, 意思是說,SQL Server在對錶Customers做Seek操作,而且是按照【Clustered Index Seek】的方式,對應的索引是【PK_Customers】,seek的值來源於[Orders].[CustomerID]

第4行:【Clustered Index Scan(OBJECT:([MyNorthwind].[dbo].[Orders].[PK_Orders]), WHERE:([MyNorthwind].[dbo].[Orders].[OrderDate]>='2010-12-01 00:00:00.000' AND [MyNorthwind].[dbo].[Orders].[OrderDate]<'2011-12-01 00:00:00.000'))】, 意思是說,SQL Server在對錶Customers做Scan操作,即:最差的【表掃描】的方式,原因是,OrderDate列上沒有索引,所以只能這樣了。

第3行:【Nested Loops(Left Outer Join, OUTER REFERENCES:([MyNorthwind].[dbo].[Orders].[CustomerID]))】, 意思是說,SQL Server把第5行和第4行產生的資料用【Nested Loops】的方式聯接起來,其中Outer表是Orders,要聯接的匹配操作也在第5行中指出了。

第2行:【Compute Scalar(DEFINE:([Expr1006]=isnull([MyNorthwind].[dbo].[Customers].[CustomerName],N'')))】, 意思是說,要執行一個isnull()函式的呼叫。具體原因請參考本文前部分中給出檢視定義程式碼。

第1行:【SELECT [v].[OrderID],[v].[CustomerID],[v].[CustomerName],[v].[OrderDate],[v].[SumMoney],[v].[Finished] FROM [OrdersView] [v] WHERE [v].[OrderDate]>[email protected] AND [v].[OrderDate]<@2】, 通常第1行就是整個查詢,表示它的返回值。

回到頂部

索引統計資訊:查詢計劃的選擇依據

前面一直說到【執行計劃】,既然是計劃,就表示要在具體執行前就能確定下來的操作方案。那麼SQL Server是如何選擇一個執行計劃的呢? SQL Server怎麼知道什麼時候該用索引或者用哪個索引呢? 對於SQL Server來說,每當要執行一個查詢時,都要首先檢查這個查詢的執行計劃是否存在快取中,如果沒有,就要生成一個執行計劃, 具體在產生執行計劃時,並不是看有哪些索引可用(隨機選擇),而是會參考一種被稱為【索引統計資訊】的資料。 如果您仔細地看一下前面的執行計劃或者執行過程表格,會發現SQL Server能預估每個步驟所產生的資料量, 正是因為SQL Server能預估這些資料量,SQL Server才能選擇一個它認為最合適的方法去執行查詢過程, 此時【索引統計資訊】就能告訴SQL Server這些資訊。 說到這裡,您是不是有點好奇呢,為了讓您對【索引統計資訊】有個感性的認識,我們來看看【索引統計資訊】是個什麼樣子的。 請在【SQL Server Management Studio】,輸入以下語句,然後執行。

dbcc show_statistics (Products, IX_CategoryID)

得到的結果如下圖:

首先,還是解釋一下命令:【dbcc show_statistics】這個命令可以顯示我們想知道的【索引統計資訊】,它需要二個引數,1. 表名,2. 索引名

再來看看命令的結果,它有三個表格組成:
1. 第一個表格,它列出了這個索引統計資訊的主要資訊。

列名 說明
Name 統計資訊的名稱。
Updated 上一次更新統計資訊的日期和時間。
Rows 表中的行數。
Rows Sampled 統計資訊的抽樣行數。
Steps 資料可分成多少個組,與第三個表對應。
Density 第一個索引列字首的選擇性(不包括 EQ_ROWS)。
Average key length 所有索引列的平均長度。
String Index 如果為“是”,則統計資訊中包含字串摘要索引,以支援為 LIKE 條件估算結果集大小。僅適用於 charvarcharnchar 和nvarcharvarchar(max)nvarchar(max)text 以及 ntext 資料型別的前導列。

2. 第二個表格,它列出各種欄位組合的選擇性,資料越小表示重複越性越小,當然選擇性也就越高。

列名 說明
All density 索引列字首集的選擇性(包括 EQ_ROWS)。注意:這個值越小就表示選擇性越高。
如果這個值小於0.1,這個索引的選擇性就比較高,反之,則表示選擇性就不高了。
Average length 索引列字首集的平均長度。
Columns 為其顯示 All density 和 Average length 的索引列字首的名稱。

3. 第三個表格,資料分佈的直方圖,SQL Server就是靠它預估一些執行步驟的資料量。

列名 說明
RANGE_HI_KEY 每個組中的最大值。
RANGE_ROWS 每組資料組的估算行數,不包含最大值。
EQ_ROWS 每組資料組中與最大值相等的行的估算數目。
DISTINCT_RANGE_ROWS 每組資料組中的非重複值的估算數目,不包含最大值。
AVG_RANGE_ROWS 每組資料組中的重複值的平均數目,不包含最大值,計算公式:RANGE_ROWS / DISTINCT_RANGE_ROWS for DISTINCT_RANGE_ROWS > 0

為了能讓您更好的理解這些資料,尤其是第三組,請看下圖:

當時我在填充測試資料時,故意把CategoryId分為1到8(10是後來臨時加的),每組填充了78條資料。所以【索引統計資訊】的第三個表格的資料也都是正確的, 也正是根據這些統計資訊,SQL Server才能對每個執行步驟預估相應的資料量,從而影響Join之類的選擇。當然了,在選擇Join方式時, 也要參考第二個表格中欄位的選擇性。SQL Server在為查詢生成執行計劃時, 查詢優化器將使用這些統計資訊並結合相關的索引來評估每種方案的開銷來選擇最佳的查詢計劃。

再來個例子說明一下統計資訊對於查詢計劃的重要性。首先多加點資料,請看以下程式碼:

declare @newCategoryId int;
insert into dbo.Categories (CategoryName) values(N'Test statistics');
set @newCategoryId = scope_identity();

declare @count int;
set @count = 0;

while( @count < 100000 )
begin
    insert into Products (ProductName, CategoryID, Unit, UnitPrice, Quantity, Remark) 
    values( cast(newid() as nvarchar(50)), @newCategoryId, N'個', 100, @count +1, N'');

    set @count = @count + 1;
end
go

update statistics Products;
go

再來看看索引統計資訊:

再來看看同一個查詢,但因為查詢引數值不同時,SQL Server選擇的執行計劃:

select p.ProductId, t.Quantity 
from Products as p left outer join [Order Details] as t on p.ProductId = t.ProductId 
where p.CategoryId = 26;    -- 26 就是最新產生的CategoryId,因此這個查詢會返回10W條記錄

select p.ProductId, t.Quantity 
from Products as p left outer join [Order Details] as t on p.ProductId = t.ProductId 
where p.CategoryId = 6;    -- 這個查詢會返回95條記錄

從上圖可以看出,由於CategoryId的引數值不同,SQL Server會選擇完全不同的執行計劃。統計資訊重要性在這裡體現的很清楚吧。

建立統計資訊後,資料庫引擎對列值(根據這些值建立統計資訊)進行排序, 並根據這些值(最多 200 個,按間隔分隔開)建立一個“直方圖”。直方圖指定有多少行精確匹配每個間隔值, 有多少行在間隔範圍內,以及間隔中值的密度大小或重複值的發生率。

SQL Server 2005 引入了對 char、varchar、varchar(max)、nchar、nvarchar、nvarchar(max)、text 和 ntext 列建立的統計資訊收集的其他資訊。這些資訊稱為“字串摘要”,可以幫助查詢優化器估計字串模式中查詢謂詞的選擇性。 查詢中有 LIKE 條件時,使用字串摘要可以更準確地估計結果集大小,並不斷優化查詢計劃。 這些條件包括諸如 WHERE ProductName LIKE '%Bike' 和 WHERE Name LIKE '[CS]heryl' 之類的條件。

既然【索引統計資訊】這麼重要,那麼它會在什麼時候生成或者更新呢?事實上,【索引統計資訊】是不用我們手工去維護的, SQL Server會自動去維護它們。而且在SQL Server中也有個引數來控制這個更新方式:

統計資訊自動功能工作方式

建立索引時,查詢優化器自動儲存有關索引列的統計資訊。另外,當 AUTO_CREATE_STATISTICS 資料庫選項設定為 ON(預設值)時, 資料庫引擎自動為沒有用於謂詞的索引的列建立統計資訊。

隨著列中資料發生變化,索引和列的統計資訊可能會過時,從而導致查詢優化器選擇的查詢處理方法不是最佳的。 例如,如果建立一個包含一個索引列和 1,000 行資料的表,每一行在索引列中的值都是唯一的, 則查詢優化器將把該索引列視為收集查詢資料的好方法。如果更新列中的資料後存在許多重複值, 則該列不再是用於查詢的理想候選列。但是,查詢優化器仍然根據索引的過時分佈統計資訊(基於更新前的資料),將其視為好的候選列。

當 AUTO_UPDATE_STATISTICS 資料庫選項設定為 ON(預設值)時,查詢優化器會在表中的資料發生變化時自動定期更新這些統計資訊。 每當查詢執行計劃中使用的統計資訊沒有通過針對當前統計資訊的測試時就會啟動統計資訊更新。 取樣是在各個資料頁上隨機進行的,取自表或統計資訊所需列的最小非聚集索引。 從磁碟讀取一個數據頁後,該資料頁上的所有行都被用來更新統計資訊。 常規情況是:在大約有 20% 的資料行發生變化時更新統計資訊。但是,查詢優化器始終確保取樣的行數儘量少。 對於小於 8 MB 的表,則始終進行完整掃描來收集統計資訊。

取樣資料(而不是分析所有資料)可以將統計資訊自動更新的開銷降至最低。 在某些情況下,統計取樣無法獲得表中資料的精確特徵。可以使用 UPDATE STATISTICS 語句的 SAMPLE 子句和 FULLSCAN 子句, 控制按逐個表的方式手動更新統計資訊時取樣的資料量。FULLSCAN 子句指定掃描表中的所有資料來收集統計資訊, 而 SAMPLE 子句用來指定取樣的行數百分比或採樣的行數

在 SQL Server 2005 中,資料庫選項 AUTO_UPDATE_STATISTICS_ASYNC 提供了統計資訊非同步更新功能。 當此選項設定為 ON 時,查詢不等待統計資訊更新,即可進行編譯。而過期的統計資訊置於佇列中, 由後臺程序中的工作執行緒來更新。查詢和任何其他併發查詢都通過使用現有的過期統計資訊立即編譯。 由於不存在等待更新後的統計資訊的延遲,因此查詢響應時間可預測;但是過期的統計資訊可能導致查詢優化器選擇低效的查詢計劃。 在更新後的統計資訊就緒後啟動的查詢將使用那些統計資訊。這可能會導致重新編譯快取的計劃(取決於較舊的統計資訊版本)。 如果在同一個顯式使用者事務中出現某些資料定義語言 (DDL) 語句(例如,CREATE、ALTER 和 DROP 語句),則無法更新非同步統計資訊。

AUTO_UPDATE_STATISTICS_ASYNC 選項設置於資料庫級別,並確定用於資料庫中所有統計資訊的更新方法。 它只適用於統計資訊更新,而無法用於以非同步方式建立統計資訊。只有將 AUTO_UPDATE_STATISTICS 設定為 ON 時, 將此選項設定為 ON 才有效。預設情況下,AUTO_UPDATE_STATISTICS_ASYNC 選項設定為 OFF。

從以上說明中,我們可以看出,對於大表,還是有可能存在統計資訊更新不及時的時候,這時,就可能會影響查詢優化器的判斷了。
有些人可能有個經驗:對於一些慢的查詢,他們會想到重建索引來嘗試解決。其實這樣做是有道理的。 因為,在某些時候一個查詢突然變慢了,可能和統計資訊更新不及時有關,進而會影響查詢優化器的判斷。 如果此時重建索引,就可以讓查詢優化器知道最新的資料分佈,自然就可以避開這個問題。 還記得我前面用【set statistics profile on】顯示的執行過程表格嗎?注意哦,那個表格就顯示每個步驟的實際資料量和預估的資料量。要不要重建索引,其實我們可以用【set statistics profile on】來看一下,如果實際資料量和預估的資料量的差值比較大, 那麼我們可以考慮手工去更新統計資訊,然後再去試試。

回到頂部

優化檢視查詢

再來說說優化檢視查詢,雖然檢視也是由一個查詢語句定義的,本質上也是一個查詢,但它和一般的查詢語句在優化時,還是有所區別的。 這裡主要的區別在於,檢視雖然是由一個查詢語句定義的,但如果只去分析這個查詢定義,可能得到的意義不大,因為檢視多數時候就不是直接使用, 而是在使用前,會加上where語句,或者放在其它語句中供from子句所使用。下面還是舉個例子吧,在我的演示資料庫中有個檢視OrdersView,定義程式碼前面有。 我們來看看,如果直接使用這個檢視,會有什麼樣的執行計劃出來:

從這個檢視可以看出,SQL Server會對錶Orders做全表掃描,應該是很低效的。再來看看下面這個查詢:

從這個執行計劃可以看出,與上面那個就不一樣了。前一個查詢中對Orders表的查詢是使用【Clustered Index Scan】的方式, 而現在在使用【Clustered Index Seek】的方式了,最右邊二個步驟的成本的百分比也發生了改變。這樣就足以說明,優化檢視時, 最好能根據實際需求,應用不同的過濾條件,再來決定如何去優化。

再來一個由三個查詢組成的情況來看看這個檢視的執行計劃。

select * from dbo.OrdersView where OrderId = 1;
select * from dbo.OrdersView where CustomerId = 1;
select * from dbo.OrdersView where OrderDate >= '2010-12-1' and OrderDate < '2011-12-1';

很明顯,對於同一個檢視,在不同的過濾條件下,執行計劃的差別很明顯。

回到頂部

推薦閱讀-MSDN文章

邏輯運算子和物理運算子引用

http://www.cnblogs.com/fish-li/archive/2011/06/06/2073626.html

相關推薦

瞭解Sql Server的執行計劃 SqlServer查詢計劃 程式設計師眼中的 SQL Server-執行計劃教會我如何建立索引?

閱讀目錄 如何啟動執行計劃 執行計劃結果要看什麼 Sql Server的五種查詢方式 檢視更具體的執行過程 參考資料   前一篇總結了Sql Server Profiler,它主要用來監控資料庫,並跟蹤生成的sql語句。但是隻拿到生成的sql語句沒有什麼用

SqlServer查詢計劃

對於SQL Server的優化來說,優化查詢可能是很常見的事情。由於資料庫的優化,本身也是一個涉及面比較的廣的話題, 因此本文只談優化查詢時如何看懂SQL Server查詢計劃。畢竟我對SQL Server的認識有限,如有錯誤,也懇請您在發現後及時批評指正。 首先,

[轉]Oracle執行計劃

嵌套循環 tle 優化器 訪問路徑 access 關聯 skip 有一個 ase 原文地址:https://www.cnblogs.com/Dreamer-1/p/6076440.html 一:什麽是Oracle執行計劃? 執行計劃是一條查詢語句在Oracle中的執行過程或

Hive的執行計劃

關於Hive執行計劃簡述 一般執行計劃有兩個部分:  stage dependencies 各個stage之間的依賴性  stage plan 各個stage的執行計劃 一個stage並不一定是一個MR,有可能是Fetch Operator,也有可能是Move Opera

嵌入式攻城獅養成計劃(三)嵌入式系統基礎之各類介面及原理圖

 Emmmmm.先寫個目錄,慢慢更新。 一、上電啟動 二、GPIO介面 GPIO——通用輸入輸出介面(General Purpose Input/Output) 上拉、下拉電阻:當GPIO引腳處於第三態(既不是輸出高電平,也不是輸

怎麼去mysql的執行計劃

程式碼託管 + 持續整合 + 敏捷管理 = 免費體驗,這僅僅只是開始>>>    mysql的檢視執行計劃的語句很簡單,explain+你要執行的sql語句就OK了。 舉一個例子 EXPLAIN SELECT * from employe

小白都能如何快速學習Elasticsearch。索引操作、新增資料、查詢資料、聚合

先匯入Elasticsearch座標 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>sp

學會這 2 點,輕鬆 MySQL 慢查詢日誌

MySQL中的日誌包括:錯誤日誌、二進位制日誌、通用查詢日誌、慢查詢日誌等等。這裡主要介紹下比較常用的兩個功能:通用查詢日誌和慢查詢日誌。 1)通用查詢日誌:記錄建立的客戶端連線和執行的語句。 2)慢查詢日誌:記錄所有執行時間超過long_query_time秒的所有查詢或者不使用索引的查詢 (1)

PL/SQL執行計劃的顯示結果

統計資訊解釋: recursive calls           遞迴呼叫,有時為了執行使用者發出的一個sql語句,Oracle必須執行一些額外的語句,我們將這些額外的語句稱之為''recursive                            

十張圖未來的大數據世界

cdb man class read get uwp 大數 社會 summary 摘要: 隨著互聯網雲時代的來臨,大數據與雲計算就像一個硬幣的正反兩面,勢必會影響到社會生活的方方面面,改變我們現有的規則和秩序。伴隨著大數據與雲計算產業的不斷發展,未來到底會變成什麽樣子?我們

redis

容易 破壞 主從同步 分鐘 來講 not policy output 禁用 【教你看懂redis配置 – 簡介】 我們可以在啟動redis-server時指定應該加載的配置文件,方法如下: 復制代碼 代碼如下: $ ./redis-server /path/to/r

如何總結

增加 反思 修改 線性表 樹和二叉樹 什麽 每一個 講解 如何 看總結流程指導:\"第1章—概述"" 1.明白數據結構的組成"" 2.理解每一個組成結構(數據邏輯結構,物理結構,算法)的具體含義\n"" 3.邏輯結構分了啥,物理結構分了啥\n" 4.算法的理解,判斷算法好

剖析執行時(讓你執行時)

init ont get tle pre art details ddc down 執行時機制:比較高級的特性,純C語言 實際上我們平時寫的OC代碼。都是轉成C語言的執行時代碼,執行時代碼的效率更高,更直接 Person.h @inter

一張圖開源許可協議,開源許可證GPL、BSD、MIT、Mozilla、Apache和LGPL的區別

軟件發布 csdn 合作 title ref 沒有 版本 簡單介紹 著名 開源許可證GPL、BSD、MIT、Mozilla、Apache和LGPL的區別 首先借用有心人士的一張相當直觀清晰的圖來劃分各種協議:開源許可證GPL、BSD、MIT、Mozilla、Apache和L

一張圖DNS域名解析全過程

能夠 title 設置 www 博文 忽略 配置 隨著 href DNS域名解析是互聯網上非常重要的一項服務,上網沖浪(還有人在用這個詞嗎?)伴隨著大量DNS服務來支撐,而對於網站運營來說,DNS域名解析的穩定可靠,意味著更多用戶的喜歡,更好的SEO效果和更大的訪問流量。我

4張圖讓你分布式架構從硬件到軟件

開發 基本 行處理 倉庫 tcp -1 管理 img 必須 對於分布式的架構相對很多開發者都是個高大上的項目,其實只要看得懂圖精通tcp通信、精通磁盤管理、精通內存管理、精通多線程與並行處理,精通事務(其實事務就是基於tcp通信層所擴展而來的MQ之類的一種IO消息模式而與)

Sqlserver 查詢語句性能測試

腳本 結果 次數 計算 span mil 輸入 高速 stat 寫程序的人,往往需要分析所寫的SQL語句是否已經優化過了,服務器的響應時間有多快,這個時候就需要用到SQL的STATISTICS狀態值來查看了。 通過設置STATISTICS我們可以查看執行SQL時的系

雲計算是什麽意思?3張圖雲計算架構

虛擬機 雲計算 固態硬盤 雲計算是一種能夠通過網絡以便利的、按需付費的方式獲取IT資源包括網絡、服務器虛機、容器、存儲、平臺、應用和服務等並提高其可用性的模式這些資源來自一個共享的、可配置的資源池並能夠以最省力和無人幹預的方式獲取和釋放。雲計算第三次IT革命雲計算發展背景雲計算特征雲計算服務類型I

一篇文章物體檢測的發展脈絡 轉

現實生活 有關 攝像頭 層次 contex 人的 圖像特征 其實在 展示 轉 https://zhuanlan.zhihu.com/p/28399320 第一,什麽是物體檢測,如何去評價一個物體裏系統的好壞。 第二,物體檢測整個的框架是怎麽樣的?它一般包含了圖像的分類

三個案例帶你LayoutInflater中inflate方法兩個參數和三個參數的區別

tco rom net roi 異常 com 組成 @override 修改 版權聲明:本文為sang原創文章,轉載請註明出處。 目錄(?)[+] 關於inflate參數問題,我想很多人多多少少都了解一點,網上也有很多關於這方面介紹的文章,但是枯燥的理論或