sqlserver聚集索引和非聚集索引並存

前面兩篇文章講解了一個數據表只存在聚集索引和只存在非聚集索引的情況,接下來我們來討論一下當聚集索引和非聚集索引同時存在的情況,這種情況也是大多數表都存在的情況。

CREATE TABLE Department11(
      DepartmentID int IDENTITY(1,1) NOT NULL PRIMARY KEY,
     Name NVARCHAR(200) NOT NULL,
     GroupName NVARCHAR(200) NOT NULL,
      Company NVARCHAR(300),
     ModifiedDate datetime NOT NULL  DEFAULT (getdate())
 )
 
 CREATE NONCLUSTERED  INDEX NCL_Name_GroupName ON [dbo].[Department11](Name,[GroupName])

DepartmentID是主鍵,即聚集索引。Name和GroupName是非聚集索引。接下來往表中插入數據:

DECLARE @i INT
SET @i=1
WHILE @i <= 1000
BEGIN
	INSERT  INTO Department11( name, [Company], groupname )
	VALUES  ( '銷售部'+CAST(@i AS VARCHAR(200)), '中國你好有限公司XX分公司', '銷售組'+CAST(@i AS VARCHAR(200)) )
	SET @i = @i + 1
END

創建用來存儲表內部存儲信息的表:

CREATE TABLE [dbo].[DBCCResult11](
	[PageFID] [nvarchar](200) NULL,
	[PagePID] [nvarchar](200) NULL,
	[IAMFID] [nvarchar](200) NULL,
	[IAMPID] [nvarchar](200) NULL,
	[ObjectID] [nvarchar](200) NULL,
	[IndexID] [nvarchar](200) NULL,
	[PartitionNumber] [nvarchar](200) NULL,
	[PartitionID] [nvarchar](200) NULL,
	[iam_chain_type] [nvarchar](200) NULL,
	[PageType] [nvarchar](200) NULL,
	[IndexLevel] [nvarchar](200) NULL,
	[NextPageFID] [nvarchar](200) NULL,
	[NextPagePID] [nvarchar](200) NULL,
	[PrevPageFID] [nvarchar](200) NULL,
	[PrevPagePID] [nvarchar](200) NULL
) 
INSERT INTO DBCCResult11 EXEC ('DBCC IND(indextest,Department11,-1) ')
select * from DBCCResult11

表的內部存儲信息如下:

 關於這個表的字段的介紹,我的前兩篇文章都有介紹,在網上也可以查到,這裏就不再重複了。

下面我們看一下非聚集索引頁的信息:

dbcc page('indextest',1,5629,3)

 

 和只有非聚集索引的表相比,沒有了RID,多了一個DepartmentID聚集索引的鍵值。

 再看一下聚集索引頁:

dbcc page('indextest',1,5631,3)

 

 聚集索引的索引頁和只有聚集索引的時候一樣。

那麼對於上面的非聚集索引頁的信息,msdn給出了下面的解釋:

如果表有聚集索引或索引視圖上有聚集索引,則行定位器是行的聚集索引鍵。

如果聚集索引不是唯一的索引,SQL Server 將添加在內部生成的值(稱爲唯一值)以使所有重複鍵唯一。

此四字節的值對於用戶不可見。僅當需要使聚集鍵唯一以用於非聚集索引中時,才添加該值。

SQL Server 通過使用存儲在非聚集索引的葉行內的聚集索引鍵搜索聚集索引來檢索數據行

書籤查找

之所以會出現書籤查找,是因爲表中建立了非聚集索引,而select查找的字段不在非聚集索引列中。所以數據庫要根據非聚集索引的行定位指針或聚集索引的鍵值去堆表或者聚集索引的葉子節點數據頁中去查詢。

根據上面的描述,書籤查詢只可能出現在非聚集索引或非聚集索引和聚集索引並存的表中,而不會出現在只有聚集索引的表中,因爲只有聚集索引的表的數據頁全都在葉子節點中。

那麼書籤查找具體是怎麼查找的呢?

select [ModifiedDate] from [dbo].[Department11] where GroupName='銷售組12'

 按道理來說這條語句應該先走非聚集索引掃描,然後走書籤查找,但是當數據量很少的時候,比如我只插入了1000條數據,這個查詢會走聚集索引掃描,因爲當數據量少的時候,走聚集索引掃描IO次數更少。

現在我把表中的數據變成10000行,再次運行這個查詢,結果如下:

 

 這個查詢流程就和我上面描述的一樣了。那麼這個Nested Loops(Inner Join)是什麼呢?

在討論聚集索引的時候我們說過,根據鍵值查詢我們只能查詢到這個鍵值所在的數據頁,並不能定位到行。那我們怎麼才能定位到行呢?

在非聚集索引的數據頁中可以根據GroupName獲取DepartmentID的值,然後根據DepartmentID的值去聚集索引的索引頁中查詢,在定位到某個數據頁後,再使用DepartmentID的值和數據頁中所有的DepartmentID做匹配查詢,即Inner Join。

 

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