天天看點

noncluster include index

包含列解析

所謂的包含列就是包含在非聚集索引中,并且不是索引列中的列。或者說的更通俗一點就是:把一些底層資料表的資料列包含在非聚集索引的索引頁中,而這些資料列又不是索引列,那麼這些列就是包含列。同時,這些包含列并不會對索引中的條目有影響。

好吧,為了使得問題稍微清楚一點,我用個簡單的圖示說明一下:

noncluster include index

我們可以用下面的語句在建立索引的時候加入包含列,代碼如下:

輕按兩下代碼全選

1

2

3

<code>create</code><code>nonclustered</code><code>index</code><code>fk_productid_ modifieddate</code>

<code>on</code><code>sales.salesorderdetail (productid, modifieddate)</code>

<code>include (orderqty, unitprice, linetotal)</code>

在上述的代碼中,productid和modifieddate包含在索引鍵中,而orderqty, unitprice, linetotal作為包含列。

下面,我們就稍微深入到頁級别來看看建立索引前後的狀态。首先,我們看看,當建立非聚集索引,但是,索引中沒有包含列的時候,索引中的索引頁的詳細如下:

noncluster include index

在上圖中可以看到,上面兩個索引頁是整個索引結構中的一部分,此時就包含了2個字段,而且這兩個字段都是索引鍵,另外一個bookmark是指向底層資料表中資料行的一個指針。

下面,我們再來看看,我們建立了有包含列的非聚集索引之後,索引頁的情況,如下圖:

noncluster include index

很明顯,原本的2個索引頁被拆分成為了3個,因為一部分底層資料行的資料的資料包含在了索引頁中。從這裡就可以知道一點:加入包含列到非聚集索引中,增大了索引結構中頁的個數,進而在使用的時候會占用更多的磁盤空間和記憶體空間。

其實把一些列作為包含列放在索引結構中就是一種用“空間換時間”的政策。

這個時候,大家可能就會問了:“何必把列放在包含列中這麼麻煩,為什麼不直接放在索引中?”。

其實把那三個列放在包含列而不是索引列中有以下幾個好處:

1. 可以使得索引鍵變化引起的波動更小。舉個例子,如果索引列中的productid或者modifieddate發生變化,那麼索引結構就會要調整,重新定 位到底層的資料行。但是,如果unitprice的值發生了變化,整個索引的結構不會發生變化,隻是在包含列中的unitprice的值進行更新而已。

2.索引中的資料列越少,資料分布的統計維護的成本就越小。

是否把一些作為索引列還是包含了其實也和資料庫的類型和用途有很大的關系。例如在oltp的資料庫中,有很多的資料的增删改寫的操作,那麼建議索引中的列不要太多。如果是warehouse類型的資料,那麼就以大量資料的讀取為主,那麼可以考慮把很一些列包含在索引列中。

包含列執行個體示範一

下面通過是三個不同的小例子作為比較示範,并且以adventureworks中的salesorderdetail示例資料表:

1.示範沒有非聚集索引的例子。

2.示範使用非聚集索引,但是沒有包含列的例子

3.示範有非聚集索引和包含列的例子

在講述的過程中,我們會結合實際的執行詳情說明問題。

示例一:沒有非聚集索引

執行語句如下:

4

5

6

7

8

9

10

11

12

13

<code>set</code><code>statistics</code><code>io</code><code>on</code>

<code>select</code><code>productid ,</code>

<code>modifieddate ,</code>

<code>sum</code><code>(orderqty)</code><code>as</code><code>'no of items'</code><code>,</code>

<code>avg</code><code>(unitprice)</code><code>'avg price'</code><code>,</code>

<code>sum</code><code>(linetotal)</code><code>'total value'</code>

<code>from</code><code>sales.salesorderdetail</code>

<code>where</code><code>productid = 888</code>

<code>group</code><code>by</code><code>productid ,</code>

<code>modifieddate ;</code>

<code>set</code><code>statistics</code><code>io</code><code>off</code>

資料結果如下:

noncluster include index

示例二:使用非聚集索引,但是沒有包含列

首先運作下面的語句,建立索引:

<code>if exists (</code><code>select</code><code>1</code><code>from</code><code>sys.indexes</code>

<code>where</code><code>name</code><code>=</code><code>'fk_productid_modifieddate'</code>

<code>and</code><code>object_id = object_id(</code><code>'sales.salesorderdetail'</code><code>) )</code>

<code>drop</code><code>index</code><code>sales.salesorderdetail.fk_productid_modifieddate</code>

<code>create</code><code>nonclustered</code><code>index</code>

<code>fk_productid_modifieddateon sales.salesorderdetail (productid, modifieddate)</code>

然後再次運作示例中的查詢。

示例三:有非聚集索引和包含列

首先執行下面的代碼:

<code>create</code><code>nonclustered</code><code>index</code><code>fk_productid_modifieddateon sales.salesorderdetail</code>

<code>(productid, modifieddate) include (orderqty, unitprice, linetotal) ;</code>

然後再次運作之前的查詢。

好了,三個例子完成之後,我們就來比較一下結果,如下:

示例 1:no nonclustered index

table 'salesorderdetail'. scan count 1, logical reads 1238.non read activity:  8%.

示例2:index – no included columns  

table 'salesorderdetail'. scan count 1, logical reads 131.non read activity:  0%.

示例3:with included columns

table 'salesorderdetail'. scan count 1, logical reads 3.non read activity:  1%.

總結如下:

1.示例1中對整個表進行掃描,每一個行都要進行掃描,是以進行大量的io活動。

2.例2首先使用非聚集索引找到productid的資料,然後通過書簽查找找到查詢中請求的其他的資料列。

3.示例3,隻要在非聚集索引中查找需要的資料就行了,因為查詢中所有列的資料都在裡面了,不用查找底層的資料表,是以查詢隻是查找了索引結構,消耗的io最少。

下面,我們就來看看第二個示例。

包含列示例示範二

第二個查詢和第一個查詢類似,隻是将where條件語句改為了按照modifieddate裡過濾。語句如下:

<code>select</code><code>modifieddate ,</code>

<code>productid ,</code>

<code>sum</code><code>(orderqty)</code><code>'no of items'</code><code>,</code>

<code>where</code><code>modifieddate =</code><code>'2003-10-01'</code>

<code>group</code><code>by</code><code>modifieddate ,</code>

<code>productid ;</code>

查詢的結果如下顯示:

noncluster include index

在查詢執行的過程中,通過where條件,選擇出了1496條資料,最後通過group語句,使得最後顯示的結果隻有164行資料。

在三種不同的情況下,運作上面的查詢,結果比較如下:

table 'salesorderdetail'. scan count 1, logical reads 1238.non read activity:  10%.

示例2:with index – no included columns

table 'salesorderdetail'. scan count 1, logical reads 761.non read activity:  8%.

我 們可以知道,第一種情況和第二種情況下面的執行計劃是一樣的,都對整表進行掃描。而在第三種情況中,因為此時的modifieddate已經包含在了索引 的包含列中,此時就沒有對整表進行掃描,而是對非聚集索引結構進行掃描。是以,可以知道:如何把一些列作為包含列放在索引中,那麼就可以在一定的程度上面 提示效率,可以把原本需要整表掃描的操作,改為非聚集索引掃描,這樣的成本更小。

摘自:http://www.dedecms.com/knowledge/data-base/sql-server/2012/0912/14133.html