--優化學習(一)
我現有一表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數據庫查詢優化(一)數據庫