天天看點

表使用

入門指南

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

注意

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

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

SELECT *

FROM big_salesSELECT ord_date

FROM big_salesStmtText

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

|--ClusteredIndexScan(OBJECT

表使用

[pubs].[dbo].[big_sales].[UPKCL_big_sales]))

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

SELECT title_id

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

|--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關鍵字操作這些行為。範圍查詢傳回的查詢計劃,與前面的直接查詢的查詢計劃很相似。下面兩個範圍查詢可提供一些資訊:

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謂詞有點不同。

Where stor_id between ‘7066’ and ‘7131’StmtText

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

表使用

[big_sales].[stor_id]>=’7066’ and ([big_sales].[stor_id]

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

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操作。