理解SQL Server的SQL查詢計劃(1)

來源:互聯網
上載者:User

入門指南

讓我們以一個簡單的例子協助你理解如何閱讀查詢計劃,可以通過發出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操作。

文章相關內容】

第一頁:入門指南

第二頁:通過比較串連和子查詢說明分支步驟  

第三頁:三種join(串連)策略


相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.