索引键列VS索引包括列

有人可以解释这两个 – 索引关键列VS索引包括列?

目前,我有一个索引,有4个索引键列和0个包含列。

谢谢

索引键列是索引的B树的一部分。 包含的列不是。

取两个索引:

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树,因为你不寻求他们,或不能因为他们不是一个允许的数据types(例如,XML),使用INCLUDE子句。

让我们来思考这本书。 书中的每一页都有页码。 本书中的所有信息都是根据此页码顺序显示的。 在数据库术语中,页码是聚簇索引。 现在想一想本书最后的词汇表。 这是按字母顺序排列的,可以让您快速find特定术语表所属的页码。 这表示包含术语表术语的非聚集索引。

现在假设每个页面都在顶部显示“章节”标题。 如果你想find词汇术语的哪一章,你必须查找什么页面描述词汇表术语,下一步 – 打开相应的页面,并看到页面上的章节标题。 这显然代表了关键的查找 – 当您需要从非索引列中查找数据时,您必须find实际的数据logging(聚簇索引)并查看此列值。 包含的列有助于提高性能 – 考虑每个章节标题包含术语表术语的词汇表。 如果您需要了解术语表的术语属于哪一章,则不需要打开实际的页面 – 查找术语表时可以find它。

所以包含的列就像那些章节标题。 非聚集索引(词汇表)具有作为非聚集索引一部分的附加属性。 索引不是按包含的列进行sorting – 它只是帮助加快查找的附加属性(例如,因为信息已经在词汇表索引中,所以不需要打开实际页面)

例:

创build表脚本

 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 (eg 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, 1000+@currentRow,@Details,@Details,@Details,@Details) set @currentRow +=1; END SET NOCOUNT OFF END GO 

使用上述SP,您可以一次插入200000条logging

您可以看到列“EnrollMentId”上有一个聚集索引。

现在在“UIDNO”列上创build一个非聚集索引。

脚本

 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. 

现在放下上面的非聚集索引并用下面的脚本重新创build

 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 

它会抛出以下错误

msg 1919,Level 16,State 1,Line 1表'dbo.Profile'中的列'Detail1'是一种无效的索引中的键列。

因为我们不能使用varchar(Max)数据types作为关键字列。

现在使用以下脚本创build包含列的非聚集索引

 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. 

包含的列不构成索引的关键字的一部分,但它们确实存在于索引中。 本质上,这些值将被复制,因此存在一定的存储开销,但索引覆盖(即由查询优化器select)更多查询的可能性更大。 查询时,这种重复也可以提高性能,因为数据库引擎可以返回值而不必查看表本身。

只有非聚簇索引可以包含列,因为在聚簇索引中,每列都被有效地包括在内。