四、分析執行計畫建立索引
根據語句的執行計畫來判斷應該對什麼表建立什麼索引,是常用優化技巧。 其實文章前面的例子已經告訴讀者如何結合statistics profile 和statistics io語句的輸出來建立索引。這裡分析乙個稍微複雜一些的例子。
sql語句如下:
select currentseno from v_ptdata_edss where mrn = @p1
statistics io的輸出如下:
table 'ptseoutpat'. scan count 2, logical reads 8, physical reads 0, read-ahead reads 0.
table 'ptdata'. scan count 1, logical reads 3218, physical reads 0, read-ahead reads 0.
部分執行計畫如下:
rows executes stmttext
0 1 select currentseno from v_ptdata_edss where mrn = @p1
0 1 |--nested loops(inner join, outer references:([ptdata].[currentseno]))
1 1 |--bookmark lookup(bookmark:([bmk1000]), object:([ttsh_neon_adt].[dbo].[ptdata]))
1 1 | |--filter(where:(convert([ptdata].[patextid])=[@p1]))
571955 1 | |--index scan(object:([ttsh_neon_adt].[dbo].[ptdata].[pk_ptdata]))
0 1 |--nested loops(inner join, outer references:([expr1009], [expr1010], [expr1011]))
2 1 |--merge interval
2 1 | |--sort(top 2, order by:([expr1012] desc, [expr1013] asc, [expr1009] asc, [exp
2 1 | |--compute scalar(define:([expr1012]=4&[expr1011]=4 and null=[expr1009],
2 1 | |--concatenation
1 1 | |--compute scalar(define:([expr1006]=null, [expr1007]=null, [ex
1 1 | | |--constant scan
1 1 | |--compute scalar(define:([expr1009]='jan 1 1900 12:00am', [ex
1 1 | |--constant scan
0 2 |--index seek(object:([ttsh_neon_adt].[dbo].[ptseoutpat].[ptseoutpat1]), seek:([pts
分析的關鍵是:
步驟1)找出最昂貴的表(也就是logical reads最多的表),是'ptdata' 表。
步驟2)從執行計畫中找出對ptdata表的相應的操作,通常是左邊行數最多的那一行如上圖中的標誌行。對錶的操作是index scan操作。
步驟3)根據操作判斷如何建立index或如何改寫語句。從執行計畫中我們看到index scan之後的操作也就是下面的filter操作把資料大大減少了:
filter(where:(convert([ptdata].[patextid])=[@p1]))
一般情況下,對這個字段建立索引問題就解決了。但對我們的例子語句而言還不夠。實際上patextid欄位已經有索引了。那麼為什麼用index scan而不用index seek呢? 後來發現原因是傳遞的引數@p1和表字段patextid的型別是不一致的。@p1是nvarchar型別,而patextid是varchar型別。這導致了sql server 產生了對索引字段進行index scan的convert操作(使用了函式,同下面)。解決方法很簡單,把傳遞的引數改成varchar或把表字段型別改成nvarchar,使得它們型別一致就可以了。
五.語句的寫法影響sql server 能否利用索引
僅僅有索引是不夠的。語句的寫法會影響sql server 對索引的選擇。比如下面的語句:
select 學生姓名, 入學時間 from tbl1 where datediff(mm,'20050301',入學時間)=1
理所當然,需要在入學時間欄位上建立索引:
create nonclustered index idx_入學時間 on tbl1(入學時間)
然後執行如下script 5看看該索引是否有用:
/******script 5***********************************/
set statistics profile on
set statistics io on
goselect 學生姓名, 入學時間 from tbl1 where datediff(mm,'20050301',入學時間)=1
goset statistics profile off
set statistics io off
語句的部分輸出如下:
table 'tbl1'. scan count 1, logical reads 385, physical reads 0, read-ahead reads 0.
rows executes stmttext
56 1 select 學生姓名, 入學時間 from tbl1 where datediff(mm,'20050301',入學
56 1 |--table scan(object:([tempdb].[dbo].[tbl1]), where:(datediff(month,
不幸的是,是table scan,剛建立的索引並沒有被使用。這是因為where語句中的datediff函式引起的。因為函式作用在索引欄位上, sql server 無法直接利用索引定位資料,必須對該字段所有的值運算該函式才能得知函式結果是否滿足where條件。在這種情況下,table scan是最好的選擇。為了使用索引,可以把語句改成如下的樣子:
select 學生姓名, 入學時間 from tbl1
where 入學時間》='20050401' and 入學時間<'20050501'
把該語句替換script 5中select語句然後執行該script,結果如下:
table 'tbl1'. scan count 1, logical reads 58, physical reads 0, read-ahead reads 0.
rows executes stmttext
56 1 select [學生姓名]=[學生姓名],[入學時間]=[入學時間] from [tbl1] where [入學時間]>=
56 1 |--bookmark lookup(bookmark:([bmk1000]), object:([tempdb].[dbo].[tbl1]) with pr
56 1 |--index seek(object:([tempdb].[dbo].[tbl1].[idx_入學時間]), seek:([tbl1].
可以看到table scan變成了index seek, logical reads 也減少到58。從上面的例子可以知道,為了利用索引,不要對where語句中的字段直接使用各種函式或表示式。要盡量把函式或表示式放在操作符的右邊。
再多舉一些例子,下面的where語句寫法是不好的:
where substring(colum1,1,4)>'ddd'
where convert(varchar(200),column1)>'aaa'
如果你實在無法避免上面的情況,而相關的語句又是資料庫系統的關鍵語句,那麼建議你從系統設計的高度來考慮問題。比方說,改變表的結構等,使得不再需要在where子句中的字段上直接使用函式或表示式等。
使用前置百分號或不等號也是不好的where寫法:
where column1 like 『%abc%』
where column1 <> 'bb'
第乙個where語句中因為第乙個百分號會導致sql server 進行索引掃瞄(index scan)或table scan。要盡量不使用前置百分號。比方說改成如下的語句就會好得多:
where column1 like 『abc%』
再多看乙個例子:
where column1 =2 or column2=30
這個where語句中如果column1 和column2中任何乙個字段沒有索引,那麼整條語句就會導致全表掃瞄。(想一想為什麼?)所以在有or的where語句要特別注意or兩邊的字段都要有必要的索引。
應用索引技術優化SQL 語句三
六 有關索引的幾個問題 問題1,是否值得在identity欄位上建立聚集索引。答案取決於identity 字段如何在語句中使用。如果你經常根據該欄位搜尋返回很少的行,那麼在其上建立索引是值得的。反之如果identity欄位根本很少在語句中使用,那麼就不應該對其建立任何索引。問題2,乙個表應該建立多少...
應用索引技術優化SQL 語句 Part 3
六 有關索引的幾個問題 問題1,是否值得在 identity欄位上建立聚集索引。答案取決於identity 字段如何在語句中使用。如果你經常根據該欄位搜尋返回很少的行,那麼在其上建立索引是值得的。反之如果identity欄位根本很少在語句中使用,那麼就不應該對其建立任何索引。問題2,乙個表應該建立多...
SQL語句優化技術分析
一 操作符優化 1 in 操作符 2 not in操作符 3 is null 或is not null操作 4 及 操作符 大於或小於操作符 乙個數值型字段a,30萬記錄的a 0,30萬記錄的a 1,39萬記錄的a 2,1萬記錄的a 3。那麼執行a 2與a 3的效果就有很大的區別了,因為a 2時or...