2010-08-27 41 views
52

有人可以解釋這兩個 - 索引關鍵列VS索引包括列?索引關鍵列VS索引包括列

目前,我有一個索引有4個索引鍵列和0個包含列。

感謝

+0

有輕微的性能改進如果您不需要的密鑰位於包含列中:https://logicalread.com/ tidbit-sql-server-indexing-part-1/ – Sun 2017-08-17 15:15:15

回答

90

索引鍵列是索引的b-tree的一部分。包含的列不是。

取兩個指標:

CREATE INDEX index1 ON table1 (col1, col2, col3) 
CREATE INDEX index2 ON table1 (col1) INCLUDE (col2, col3) 

index1是更適合此類型的查詢:

SELECT * FROM table1 WHERE col1 = x AND col2 = y AND col3 = z 

index2是更適合此類型的查詢:

SELECT col2, col3 FROM table1 WHERE col1 = x 

在第一個查詢中,index1提供了一個快速識別感興趣的行的機制。查詢將(可能)作爲索引查找來執行,然後是書籤查找來檢索整行。

在第二個查詢中,index2充當覆蓋索引。因爲索引提供了滿足查詢所需的所有數據,所以SQL Server根本不必命中基本表。在這種情況下,index1也可以充當覆蓋索引。

如果你想要一個覆蓋索引,但不想將所有列添加到B樹,因爲你不在它們上面尋找,或者不能,因爲它們不是允許的數據類型(例如,XML ),請使用INCLUDE子句。

6

包括列不構成索引鍵的一部分,但它們對指數存在。本質上,這些值將被複制,因此存在一定的存儲開銷,但索引將覆蓋(即由查詢優化器選擇)更多查詢的可能性更大。這種重複也可以在查詢時提高性能,因爲數據庫引擎可以返回值而不必查看錶本身。

只有非聚簇索引可以包含列,因爲在聚簇索引中,每個列都被有效包含。

+0

+1 IOW覆蓋索引的行密度更高,這意味着SQL需要獲取更少的頁面。 – StuartLC 2010-08-27 04:37:06

+0

@nonnb:使用覆蓋索引,不需要書籤查找。是的,讀取的頁面更少,並且SQL Server不得不做更少的工作,但這是因爲它需要的所有信息都包含在索引中。對於其中的一些定義,「信息密度」可能更高。不過,不清楚你的行密度是什麼意思。 – 2010-08-27 04:43:02

11

讓我們來思考一下這本書。書中的每一頁都有頁碼。本書中的所有信息都是根據此頁碼順序呈現。在數據庫術語中,頁碼是聚集索引。現在想一想本書最後的詞彙表。這是按字母順序排列的,可讓您快速找到特定術語表所屬的頁碼。這表示包含術語表術語的非聚集索引作爲關鍵列。

現在假設每頁也在頂部顯示「章節」標題。如果您想查找詞彙術語的哪一章,則必須查找術語術語術語的下一頁 - 打開相應的頁面並查看頁面上的章節標題。這顯然代表了關鍵查找 - 當您需要從非索引列中查找數據時,您必須找到實際數據記錄(聚集索引)並查看此列值。包含的列在性能方面有所幫助 - 請考慮除詞彙表術語外每個章節標題包含的詞彙表。 如果您需要了解術語表的術語屬於哪一章 - 則不需要打開實際頁面 - 查找術語表時可以獲取它。

所以包含的列就像那些章節標題。非聚集索引(詞彙表)具有添加屬性作爲非聚集索引的一部分。索引不是按包含的列排序 - 它只是幫助加快查找的附加屬性(例如,因爲信息已在詞彙表索引中,所以不需要打開實際頁面)

例子:

創建表腳本

CREATE TABLE [dbo].[Profile](
    [EnrollMentId] [int] IDENTITY(1,1) NOT NULL, 
    [FName] [varchar](50) NULL, 
    [MName] [varchar](50) NULL, 
    [LName] [varchar](50) NULL, 
    [NickName] [varchar](50) NULL, 
    [DOB] [date] NULL, 
    [Qualification] [varchar](50) NULL, 
    [Profession] [varchar](50) NULL, 
    [MaritalStatus] [int] NULL, 
    [CurrentCity] [varchar](50) NULL, 
    [NativePlace] [varchar](50) NULL, 
    [District] [varchar](50) NULL, 
    [State] [varchar](50) NULL, 
    [Country] [varchar](50) NULL, 
    [UIDNO] [int] NOT NULL, 
    [Detail1] [varchar](max) NULL, 
    [Detail2] [varchar](max) NULL, 
    [Detail3] [varchar](max) NULL, 
    [Detail4] [varchar](max) NULL, 
PRIMARY KEY CLUSTERED 
(
    [EnrollMentId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 

GO 

SET ANSI_PADDING OFF 
GO 

存儲過程腳本

CREATE Proc [dbo].[InsertIntoProfileTable] 
As 
BEGIN 
SET NOCOUNT ON 
Declare @currentRow int 
Declare @Details varchar(Max) 
Declare @dob Date 
set @currentRow =1; 
set @Details ='Let''s think about the book. Every page in the book has the page number. All information in this book is presented sequentially based on this page number. Speaking in the database terms, page number is the clustered index. Now think about the glossary at the end of the book. This is in alphabetical order and allow you to quickly find the page number specific glossary term belongs to. This represents non-clustered index with glossary term as the key column.  Now assuming that every page also shows "chapter" title at the top. If you want to find in what chapter is the glossary term, you have to lookup what page # describes glossary term, next - open corresponding page and see the chapter title on the page. This clearly represents key lookup - when you need to find the data from non-indexed column, you have to find actual data record (clustered index) and look at this column value. Included column helps in terms of performance - think about glossary where each chapter title includes in addition to glossary term. If you need to find out what chapter the glossary term belongs - you don''t need to open actual page - you can get it when you lookup the glossary term.  So included column are like those chapter titles. Non clustered Index (glossary) has addition attribute as part of the non-clustered index. Index is not sorted by included columns - it just additional attributes that helps to speed up the lookup (e.g. you don''t need to open actual page because information is already in the glossary index).' 
while(@currentRow <=200000) 
BEGIN 
insert into dbo.Profile values('FName'+ Cast(@currentRow as varchar), 'MName' + Cast(@currentRow as varchar), 'MName' + Cast(@currentRow as varchar), 'NickName' + Cast(@currentRow as varchar), DATEADD(DAY, ROUND(10000*RAND(),0),'01-01-1980'),NULL, NULL, @currentRow%3, NULL,NULL,NULL,NULL,NULL, [email protected],@Details,@Details,@Details,@Details) 
set @currentRow +=1; 
END 

SET NOCOUNT OFF 
END 

GO 

使用上述SP可以插入200000條記錄一次。

您可以看到列「EnrollMentId」上有一個聚簇索引。

現在在「UIDNO」列上創建一個非聚簇索引。

腳本

CREATE NONCLUSTERED INDEX [NonClusteredIndex-20140216-223309] ON [dbo].[Profile] 
(
    [UIDNO] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

現在運行下面的查詢

select UIDNO,FName,DOB, MaritalStatus, Detail1 from dbo.Profile 
--Takes about 30-50 seconds and return 200,000 results. 

查詢2

select UIDNO,FName,DOB, MaritalStatus, Detail1 from dbo.Profile 
where DOB between '01-01-1980' and '01-01-1985' 
--Takes about 10-15 seconds and return 36,479 records. 

現在拖放上述非聚集索引,並與下面的腳本

重新創建
CREATE NONCLUSTERED INDEX [NonClusteredIndex-20140216-231011] ON [dbo].[Profile] 
(
    [UIDNO] ASC, 
    [FName] ASC, 
    [DOB] ASC, 
    [MaritalStatus] ASC, 
    [Detail1] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

它會引發以下錯誤

消息1919年,級別16,狀態1,行 列「Detail1」表「dbo.Profile」是一類是無效的使用作爲索引中的關鍵列。

因爲我們不能使用varchar(Max)數據類型作爲關鍵字列。

現在使用下面的腳本包含的列

CREATE NONCLUSTERED INDEX [NonClusteredIndex-20140216-231811] ON [dbo].[Profile] 
(
    [UIDNO] ASC 
) 
INCLUDE ( [FName], 
    [DOB], 
    [MaritalStatus], 
    [Detail1]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

現在運行下面的查詢創建一個非聚集索引

select UIDNO,FName,DOB, MaritalStatus, Detail1 from dbo.Profile --Takes about 20-30 seconds and return 200,000 results. 

查詢2

select UIDNO,FName,DOB, MaritalStatus, Detail1 from dbo.Profile 
where DOB between '01-01-1980' and '01-01-1985' 
--Takes about 3-5 seconds and return 36,479 records. 
+4

http://social.msdn.microsoft.com/Forums/sqlserver/en-US/7fb76e54-4912-48fa-8816-56878e88a176/whats-the-difference-between-index-key-columns-and-include-columns - 當創造-的指數?論壇= sqldatabaseengine – mrd3650 2014-04-25 15:47:59