理解SQL Server的SQL查詢計劃

讓我們以一個簡單的例子幫助你理解如何閱讀查詢計劃,可以通過發出SET SHOWPLAN_TEXT On命令,或者在SQL Query Analyzer 的配置屬性中設置同樣的選項等方式得到查詢計劃。

注意:這個例子使用了表pubs.big_sales,該表與pubs..sales表完全相同,除了多了80000行的記錄,以當作簡單explain plan例子的主要數據。

如下所示,這個最簡單的查詢將掃描整個聚集索引,如果該索引存在。注意聚集鍵值是物理次序,數據按該次序存放。所以,如果聚集鍵值存在,你將可能避免對整個表進行掃描。即使你所選的列不在聚集鍵值中,例如ord_date,這個查詢引擎將用索引掃描並返回結果集。

SELECT *

FROM big_sales

SELECT ord_date

FROM big_sales

StmtText

-------------------------------------------------------------------------

|--ClusteredIndexScan(OBJECT:([pubs].[dbo].[big_sales].[UPKCL_big_sales]))

上面的查詢展示返回的數據量非常不同,所以小結果集(ord_date)的查詢比其它查詢運行更快,這只是因爲存在大量底層的I/O。然而,這兩個查詢計劃實際上是一樣的。你可以通過使用其它索引提高性能。例如,在title_id列上有一個非聚集索引存在:

SELECT title_id

FROM big_sales

StmtText

------------------------------------------------------------------

|--Index Scan(OBJECT:([pubs].[dbo].[big_sales].[ndx_sales_ttlID]))

上面的查詢的執行時間與SELECT *查詢相比非常小,這是因爲可以從非聚集索引即可得到所有結果。該類查詢被稱爲covering query(覆蓋查詢),因爲全部結果集被一個非聚集索引所覆蓋。

SEEK與SCAN

第一件事是你需要在查詢計劃中區別SEEK和SCAN操作的不同。

注意:一個簡單但非常有用的規則是SEEK操作是有效率的,而SCAN操作即使不是非常差,其效率也不是很好。SEEK操作是直接的,或者至少是快速的,而SCAN操作需要對整個對象進行讀取(表,聚集索引或非聚集索引)。因此,SCAN操作通常比SEEK要消耗更多的資源。如果你的查詢計劃僅是掃描操作,你就應該考慮調整你的查詢了。

where子句在查詢性能中能產生巨大的差異,如下面展示的:

Select *

From big_sales

Where stor_id=’6380’

StmtText

-----------------------------------------------------------------------------|--Clustered

Index Seek(OBJECT: ([pubs].[dbo].[big_sales].[UPKCL_big_sales])),

SEEK: ([big_sales].[stor_id]={@1} ORDERED FORWARD)

上面的查詢是在聚集索引上執行SEEK而不是SCAN操作。這個SHOWPLAN確切的描述SEEK操作是基於stor_id並且結果是按照在索引中存儲的順序排序的。因爲SQL Server支持索引的向前和向後滾動的性能是相同的,所以你可以在查詢計劃中看到ORDERED FORWARD 或ORDERED BACKWARD。這只是告訴你表或索引讀取的方向。你甚至可以在ORDER BY子句中通過用ASC和DESC關鍵字操作這些行爲。範圍查詢返回的查詢計劃,與前面的直接查詢的查詢計劃很相似。下面兩個範圍查詢可提供一些信息:

Select *

From big_sales

Where stor_id>=’7131’

StmtText

------------------------------------------------------------------------------|-Clustered

Index Seek(OBJECT: ([pubs].[dbo].[big_sales].[UPKCL_big_sales] ),

SEEK: ([big_sales].[stor_id]>=’7131’) ORDER FORWARD

上面的查詢看起來很象以前的例子,除了SEEK謂詞有點不同。

Select *

From big_sales

Where stor_id between ‘7066’ and ‘7131’

StmtText

------------------------------------------------------------------------------|-Clustered

Index Seek(OBJECT: ([pubs].[dbo].[big_sales].[UPKCL_big_sales] ),

SEEK:([big_sales].[stor_id]>=’7066’ and ([big_sales].[stor_id]<=’7131’) ORDER FORWARD)

這個看起來也一樣。只是查找謂詞改變了。因爲查找是非常快的,所以這個查詢是相當好的。

SEEK和SCAN也可包含Where謂詞。在這種情況下,這個謂詞告訴你Where子句從結果集中過濾出哪些記錄。因爲它是作爲SEEK或SCAN的一個組件執行的, Where子句通常既不損害也不提高這個操作本身的性能。Where子句會幫助查詢優化器找到可能有最佳性能的索引。

查詢優化的一個重要部分是要確定是否在某個索引上執行SEEK操作,如果是這樣,就找到了具有最佳性能的索引。大部分情況下,查詢引擎能出色地查找到存在的索引。但是,目前有三種涉及到索引的常見問題:

數據庫設計師,通常是應用開發者,在表中沒有建立任何索引。

◆數據庫設計師通常猜測不到常用的查詢或事務類型,所以建立在表上的索引或主鍵往往效率不高。

◆當索引表被創建時,即使數據庫設計師猜測較準,但事務負載隨着時間將發生改變,使得這些索引效率變差。

如果你在你的查詢計劃中看到大量的SCAN而不是SEEK,你應該從新評估你的索引。例如,看看下面的查詢:

Select ord_num

From sales

Where ord_date IS NOT NUL

And ord_date>’Jan 01,2002 12:00:00 AM’

StemtText

----------------------------------------------------------------------------------|--

Clustered Index Scan(OBJECT: ([pubs].[dbo].[sales].[UPKCL_sales] ),

WHERE : ([sales].[ord_date]>’Jan 1,2002 12:00:00 AM ’))

現在這個查詢在我們剛創建的sales_ord_date索引上執行SEEK INDEX操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章