應用索引技術優化SQL 語句 Part 2

2022-01-24 08:09:02 字數 4339 閱讀 4945

四、分析執行計畫建立索引

根據語句的執行計畫來判斷應該對什麼表建立什麼索引,是常用優化技巧。 其實文章前面的例子已經告訴讀者如何結合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...