我要用做例子的是下面这个叫CompRpt.DimBAN的表,它的结构如下:
它是一个基于BANKey的聚集索引表,另外还有一个非聚集索引,该非聚集索引以BANNumber为key,并且include了以下的列:
使用DBCC IND后得到的结果大约如下:
我们上面已经说过来,该表是一个聚集索引索引表,另外还有一个非聚集索引,所以首先我们以IndexID和PageType来分组,结果如下:
可以看出对于聚集索引(IndexID = 1)来说,它使用了3种类型的页面:数据页(PageType = 1)、索引页(PageType = 2)和IAM页(PageType = 10)。对于非聚集索引(IndexID = 2) 来说,它使用了2种类型的页面:索引页 (PageType = 2) 和IAM页 (PageType = 10) 。
到这里我们可以小总结一下就是:
· 聚集索引会使用3种页,IAM页面用来记录所有分配给以及预留给该索引使用的页;而聚集索引这个二叉树的非叶子结点都是索引页;聚集索引的叶子结点都是数据页
· 非聚集索引使用2种类型的页,IAM页同聚集索引的中的IAM页的作用一样,而非聚集索引的二叉树的结点,不论是叶子还是非叶子结点都是索引页
IAM页的内容
正如前面说到的,聚集索引总共用到了3种类型的页,我们首先看一下IAM类型的页,下面是该表的聚集索引用到的所有的IAM页:
我们可以用每一行的NextPagePID和PrePagePID把该索引用到的所有IAM页串联起来形成以下的IAM链:
DBCC Page (BenefitsRpt, 1, 292805, 3) WITH TableResults
下面是该页面内容的一部分:
可以看出该IAM页中存储的就是指向其他分配给该表的页面的指针以及页面是否已经分配给了表还是未分配给表示用。
我们以上面被红色高亮的行为例子,该行说页面(1:803424)已经分配给了该聚集索引使用。那么我们在DBCC IND所列出的所有页面中可以找到该页面,如下:
可以看出,正如IAM中指示的,该页面已经分配给了IndexID = 1的聚集索引,并且该页面是数据页(PageType = 1)。同样的,我们也可以查看其他的在IAM页中指示已分配的页面也是都在DBCC IND中列了出来。
到这里,我们应该可以说,每个表以及其索引都有一个IAM链来维护着所有的分配给以及预留给该表或索引的页面;也就是说IAM页中保存的页面没有指示索引的顺序?
索引页的内容
我们现在查看一下索引页中存储的东西,就以下面这个属于非聚集索引的,并且IndexLevel =2的索引页为例子,其实可以看出来这个索引页是非聚集索引的根结点,因为它没有NextPage和PrePage:
该页面保存的内容如下:
其中每一行就是该索引页面的一个索引项,而每一个索引项保存的东西对我们来说主要有三个:
· ChildPageId
我们前面说了,该页面是非聚集索引的根结点,该ChidPageId保存的就是该索引项指向其下一级索引页的指针
· BANNumber
该非聚集索引的Key键就是BANNumber,所以在索引的非叶子索引页上每一个索引项保存的就是BANNumber的值
· BANKey
这个指向了对应于该条索引项的聚集索引键值,这是为了做Key Lookup用的
我们以上面第二个索引项为例子来看一下,它的索引键值(BANNumber)是13969316,它指示在二叉树上它的下一级索引页的ID是828114,
我们在DBCC IND列出的页面中查找该页面的下一个页面是什么
可以看出其NextPage是814925
我们再看一下上面该非聚集索引列出的内容:
上面第二行就是828114,它保存的BANNumber是13969316,第三行正好是814925,它的BANNumber是14648414,那么意味着828114这个页面保存的BANNumber的值应该是在13969316和14648414之间,到底是不是这样呢?我们使用DBCC Page来查看一下:
DBCC Page(BenefitsRpt,1,828114,3) WITH TableResults
结果如下,可以看到该页面确实存储的是13969316到14647002(小于14648414)之间的值:
不过有一点注意的就是,在上以级索引项中保存了BANNumber是13969316了,它指示的下一级索引页面在第一项就也保存了这个BANNumber。这是很容易理解的,因为除非查询语句只是需要返回索引键值,不然最终是要通过根索引查找到叶子结点来返回被include的列的,或者是还要进一步查找到聚集索引返回其他的列的,因而索引的叶子结点上肯定会包含所有的键值。
好,然后在这个第二级的索引页中,它指示保存BANNumber(13969316)的下一级索引页为805149
我们再查看一下805149页面的内容,如下:
可以看出,它的结构与上面不一样了,它没有了ChildPageId列,因为它是索引的叶子级别了;而多了另外一些列(BANAssignedDate, IsTechSupportAssigned等),这是该索引的包含列;还有BANKey,这是该索引项对应的聚集索引的键值。
所以,总结一下就是,如果我们有如下的一个表:
PK Column | ID | Include column 1 | Include column 2 |
PK 1 | 1 | a | a |
PK 2 | 2 | b | b |
PK 3 | 3 | c | c |
PK 4 | 4 | d | d |
它的PK Column是主键列,然后我们建一个基于ID列并且include了后面两列的非聚据索引,那么非聚集索引的结果很像下面的结构: