本節方法適用於 SQL Server Management Studio 2005 及之後的版本。
取得執行計畫#
SQL Server 有數種取得執行計畫的方式,其中最重要的兩種:
- 圖形化:在 Management Studio 中很容易取得,但難以分享——述詞資訊只有把滑鼠移到特定操作上(hover)時才看得到。
- 表格式:閱讀較困難,但容易複製,因為它一次呈現所有相關資訊。
圖形化#
用工具列上的兩個按鈕之一產生:左邊的按鈕直接解釋當前選取的敘述,右邊的則會在下一次執行 SQL 時擷取計畫。兩者的圖形結果都出現在「Results」窗格的「Execution plan」分頁。
圖形表示法稍加練習後很好讀,但它只顯示最基本的資訊:操作,以及它作用的資料表或索引。把滑鼠移到操作上時 Management Studio 才顯示更多資訊——這也使得完整分享執行計畫變得困難。
表格式#
表格式的執行計畫透過剖析(profiling)敘述執行來取得:
SET STATISTICS PROFILE ON啟用後,每個執行的敘述都會多產生一個結果集。例如 select 會產生兩個結果集——先是敘述本身的結果,接著是執行計畫。
表格式計畫在 SQL Server Management Studio 中幾乎不堪使用,因為
StmtText欄位太寬、放不進螢幕。
它的優勢在於複製時不會遺失相關資訊——想把 SQL Server 執行計畫貼到論壇之類的平台時非常方便。通常只要複製 StmtText 欄位並稍加排版即可:
select COUNT(*) from employees;
|--Compute Scalar(DEFINE:([Expr1004]=CONVERT_IMPLICIT(...))
|--Stream Aggregate(DEFINE:([Expr1005]=Count(*)))
|--Index Scan(OBJECT:([employees].[employees_pk]))最後再關閉剖析:
SET STATISTICS PROFILE OFF操作#
索引與資料表存取#
SQL Server 的術語很簡單:「Scan」操作讀取整個索引或資料表,「Seek」操作則用 B-tree 或實體位址(RID,類似 Oracle 的 ROWID)存取索引或表的特定部分。
Index Seek:做 B-tree 走訪並走過葉節點找出所有匹配條目。Index Scan:依索引順序讀取整個索引(所有列)。當需要「依索引順序取得所有列」時(例如對應的 order by)可能採用。Key Lookup (Clustered):從叢集索引取回單一列,類似 Oracle 對索引組織表(IOT)的INDEX UNIQUE SCAN。RID Lookup (Heap):從資料表取回單一列,相當於 Oracle 的TABLE ACCESS BY INDEX ROWID。Table Scan:即全表掃描,以磁碟儲存順序讀取整張表。雖然多區塊讀取能大幅提升速度,它仍是最昂貴的操作之一——除了高 IO,還必須檢視所有列,消耗可觀的 CPU 時間。
Join#
join 一次只處理兩張表;查詢中有更多 join 時依序執行。因此「表」也可能意指「中間結果」。
Nested Loops:從一張表取得結果,再為每一列查詢另一張表。SQL Server 也用這個操作在索引存取後取回資料表資料。Hash Match:把 join 一側的候選記錄載入雜湊表,再以另一側的每一列探測。Merge Join:像拉鍊一樣合併兩份已排序的清單,兩側都必須預先排序。
排序與分組#
Sort:依 order by 排序結果。需要大量記憶體具體化中間結果(非管線化)。Sort (Top N Sort):依 order by 排序結果的一個子集。用於無法管線化執行的 top-N 查詢。Stream Aggregate:對已排序的集合依 group by 彙總。不緩衝中間結果,以管線化方式執行。Hash Match (Aggregate):用雜湊表分組。需要大量記憶體具體化中間結果(非管線化),且輸出沒有任何有意義的排序。
Top-N 查詢#
Top:抓到所需列數時中止底層操作。
Top-N 查詢的效率取決於底層操作的執行模式——中止
Sort這類非管線化操作時效率極差。
區分存取述詞與篩選述詞#
SQL Server 同樣以三種方式套用 where 子句:
- 存取述詞(
Seek Predicates):葉節點走訪的起訖條件。 - 索引篩選述詞(索引操作上的
Predicates或where):只在葉節點走訪期間套用,不縮小掃描範圍。 - 資料表層級篩選述詞(資料表操作上的
where):欄位不屬於索引,必須先從堆積表載入該列才能求值。
以第 3 章示範索引篩選述詞影響的範例為基礎:
CREATE TABLE scale_data (
section NUMERIC NOT NULL,
id1 NUMERIC NOT NULL,
id2 NUMERIC NOT NULL
);
CREATE INDEX scale_slow ON scale_data(section, id1, id2);
SELECT count(*)
FROM scale_data
WHERE section = @sec
AND id2 = @id2在圖形化計畫中#
圖形化計畫把述詞資訊藏在工具提示裡,只有把滑鼠移到 Index Seek 操作上才會顯示。
SQL Server 的
Seek Predicates對應 Oracle 的存取述詞——它們縮小葉節點走訪範圍。篩選述詞在圖形化計畫中則只標示為Predicates。
在表格式計畫中#
表格式計畫把述詞資訊放在與操作同一欄,因此很容易一次複製所有相關資訊:
DECLARE @sec numeric;
DECLARE @id2 numeric;
SET STATISTICS PROFILE ON
SELECT count(*)
FROM scale_data
WHERE section = @sec
AND id2 = @id2
SET STATISTICS PROFILE OFF執行計畫作為第二個結果集出現。以下是稍加排版的 StmtText 欄位:
|--Compute Scalar(DEFINE:([Expr1004]=CONVERT_IMPLICIT(...))
|--Stream Aggregate(DEFINE:([Expr1008]=Count(*)))
|--Index Seek(OBJECT:([scale_data].[scale_slow]),
SEEK: ([scale_data].[section]=[@sec])
ORDERED FORWARD
WHERE:([scale_data].[id2]=[@id2]))