MSSQL數據庫查詢優化(一)

--優化學習(一)
 
我現有一表Orders,其中包含OrderId,UserId,CreateDate,TotalMoney,OrderType五個字段,
 
目前沒有主鍵和其餘索引
 
現在我想查詢出在指定某個日期的訂單數量,並返回OrderId,UserId,TotalMoney三字段,具體
 
查詢語句以下:
 
select
 OrderId,
 UserId,
 TotalMoney
from
 Orders
where CreateDate='2012-12-17 14:59:53.463'
 
這個時候我執行如下查詢語句,觀察一下執行計劃:
 
 圖-1

此時執行計劃顯示錶掃描.分析表的數據存儲結構,該表無主鍵和索引,數據存放形式
 
爲堆存儲,在查詢的時候會讀取表的每一行,在CreateDate中評估此謂詞,爲真則返回
 
此行數據,輸出列OrderId,UserId,TotalMoney
 
在這個查詢中,掃描表操做涉及到該表的全部數據行,無論該行數據是否知足條件,因此
 
掃描的開銷是與表的數據量成正比的.若是在表的數據量不多的時候或者知足where後
 
謂詞的數據行比例較大時,這樣的執行計劃是有效的,可是若是表的數據量大,且符合條
 
件的數據比例很小,那麼這樣的掃描顯然是會涉及到不少沒必要要的數據頁與行,同時形成
 
不少沒必要要的I/O開銷
 
這時,我首先對錶Orders加上一個彙集索引
 
create clustered index Orders_OrderId_idxon Orders(OrderId)
go
 
如今表的數據存放是按照B-Tree結構存儲,如今執行該查詢語句,查看執行計劃:
 

  圖-2
這個時候觀察執行計劃,咱們能夠清晰地發現以前執行計劃中出現的表掃描消失,取而代之的是
 
彙集索引掃描,那麼這二者之間有什麼區別?

當表不存在彙集索引的時候,是堆表.當創建彙集索引後,表是以B-Tree形式存儲
 
而我在添加主鍵的時候,主鍵字段默認彙集索引,這個時候會默認建立統計信息,系統表 sys.stats
 
中也會出現.這個時候優化器會選擇彙集索引掃描,而不是以前的表掃描.但從I/O開銷上來講,並無
 
帶來優化.這是因爲聚焦索引和數據存儲在一塊兒, 決定表數據的物理存儲順序, 一個表只能有一個聚
 
集索引, 其葉子結點是數據行,彙集索引掃描的是索引頁,而索引頁存放整個表(整個表的一個副本),
 
經過彙集索引找到一條記錄的時候, 這條記錄相關的列的值也能夠直接取出來,而表掃描則是掃描RID,
 
也是同數據存放在一塊兒的,掃描到每一行的時候,也能夠直接取出該行數據的值,二者在開銷上並無太
 
大的區別
 
如今我在字段CreateDate字段上建立一個非彙集索引:
 
create nonclustered index Orders_CreateDate_idxon Orders(CreateDate)
go
 
這個時候的執行計劃如圖
 

  圖-3

從該執行計劃中發現,以前的彙集索引掃描已經消失,取而代之的是費彙集索引查找和檢查找兩部分,而且
 
分別佔到總開銷的%左右
 
以下圖:
 

  圖-4



  圖-5

那麼不少人可能在這個時候誤認爲這已是最優的執行計劃了,但其實否則。咱們分析能夠看出,圖-4

和圖-5咱們能夠看出,索引查找這一部分只輸出了OrderId這一個字段,而檢查找是用與輸出UserId和
 
TotalMoney兩個字段。形成這個執行計劃出現的緣由是:
 
非彙集索引單獨存儲,若是查詢的結果引用了非聚焦索引不包括的那些列,那麼非聚焦索引還須要經過行
 
定位器去表中取該記錄對應的列的數據, 這裏面就有一個再次查找的問題。而這個時候咱們的查詢語句
 
返回了三個字段,第一個爲彙集索引所在的字段,第二個和第三個字段上沒有任何索引。那麼爲何返回
 
的彙集索引字段沒有出現二次查找的狀況呢?這是因爲在OrderId字段簡歷彙集索引後,在這個表建立的
 
其它全部的非彙集索引都已經覆蓋了彙集索引字段,具體原則以下圖:
 

 圖-6

這個時候爲了返回兩個非索引覆蓋字段,優化器就會經過二次查找返回這兩個字段,而二次查找則根據
 
以前創建的彙集索引來實現,因而出現了鍵查找這一迭代器。

此時,咱們若是讓索引覆蓋這兩個字段就能夠避免出現鍵查找的出現。

drop index Orders_CreateDate_idx onOrders
create nonclustered index Orders_CreateDate_idxon Orders(CreateDate) include(UserId,TotalMoney)
go
 
這個時候咱們再來查看執行計劃:
 

  圖-7

如今以前出現的部分已經被索引查找給取代,鍵查找部分的開銷也已經被優化掉:
 
 圖-8

在這個例子中,若是咱們不建立彙集索引,那麼又是會什麼樣呢?

我刪掉以前的索引,而後從新建立一個只包含CreateDate字段的非彙集索引
 
drop index Orders_CreateDate_idx onOrders
drop index Orders_OrderId_idx onOrders
create nonclustered index Orders_CreateDate_idxon Orders(CreateDate)
go
 
接下來看執行計劃:
 
 圖-9

咱們比較以前的圖-3能夠發現,二者的區別在於如今是RID查找,以前是鍵查找。其實這樣的
 
差異的出現是因爲如今的表是堆表,二次查找須要經過行定位符去查找索引不包含的那兩列,
 
而以前存在彙集索引,優化器選擇了彙集索引鍵查找返回了那兩列數據。

這個時候咱們也只需修改非彙集索引,讓它包含這兩個字段便可達到目的。

drop index Orders_CreateDate_idx onOrders
create nonclustered index Orders_CreateDate_idxon Orders(CreateDate) include(OrderId,UserId,TotalMoney)
go
 
執行計劃如圖:
 
 圖-10

這裏你們須要認真觀察我每一個索引的建立,特別注意存在彙集索引和不存在彙集索引的時候。

不論是RID SEEK仍是CLUSTERED SEEK,通常狀況下咱們均可以選擇讓索引包含這些返回字段的
 
方法優化他們,從而避免二次查找的出現,力求I/O開銷達到最優。html

文章來源:MSSQL數據庫查詢優化(一)數據庫

相關文章
相關標籤/搜索