【问题标题】:SQL Server 2008 included columnsSQL Server 2008 包含的列
【发布时间】:2013-09-02 19:16:35
【问题描述】:

我最近发现 SQL Server 索引中包含列。索引中包含的列是否会占用额外的内存,或者它们是否存储在磁盘上?

还有人可以指出在主键中包含不同数据类型的列作为包含列的性能影响,在我的情况下,这通常是一个 in?

谢谢。

【问题讨论】:

    标签: sql sql-server indexing


    【解决方案1】:

    我不完全理解这个问题:“索引中包含的列会占用额外的内存还是存储在磁盘上?”索引既存储在磁盘上(用于持久性),也存储在内存中(用于使用时的性能)。

    您的问题的答案是非键列存储在索引中,因此与索引的其余部分一起存储在磁盘和内存中。与索引中的键列相比,包含列确实具有显着的性能优势。要了解这一优势,您必须了解键值可能会多次存储在 b-tree 索引结构中。它们既用作树中的“节点”,也用作“叶子”(后者指向表中的实际记录)。非键值仅存储在叶子中,从而可能节省大量存储空间。

    这样的节省意味着可以在内存有限的环境中将更多的索引存储在内存中。并且索引占用更少的内存,允许内存用于其他事情。

    使用包含列是为了让索引成为查询的“覆盖”索引,而额外的开销最小。当查询所需的所有列都在索引中时,索引会“覆盖”查询,因此可以使用索引来代替原始数据页。这可以显着节省性能。

    了解更多关于它们的地方是微软documentation

    【讨论】:

      【解决方案2】:

      在 SQL Server 2005 或更高版本中,您可以通过将非键列添加到非聚集索引的叶级来扩展非聚集索引的功能。

      通过包含非键列,您可以创建涵盖更多查询的非聚集索引。这是因为非键列具有以下好处:

      • 它们可以是不允许作为索引键列的数据类型。 (除了 textntextimage 之外的所有数据类型都允许。)

      • 在计算索引键列数或索引键大小时,数据库引擎不会考虑它们。您可以在非聚集索引中包含非键列,以避免超出最多 16 个键 列和最大索引键大小 900 字节的当前索引大小限制。

      当查询中的所有列都作为键或非键列包含在索引中时,包含非键列的索引可以显着提高查询性能。性能提升是因为查询优化器可以定位索引中的所有列值;不访问表或聚集索引数据,从而减少磁盘 I/O 操作。

      例子:

      Create Table Script
      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, 1000+@currentRow,@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,第 1 行 表 'dbo.Profile' 中的列 'Detail1' 的类型不能用作索引中的键列。

      因为我们不能使用 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.
      

      【讨论】:

        【解决方案3】:

        包含列提供类似于聚集索引的功能,其中行内容保存在主索引的叶节点中。除了索引中的键列外,索引表叶节点中还保留了其他属性。

        这允许立即访问列值,而无需访问数据库中的另一个页面。增加索引大小和一般存储与不必通过索引中的页面引用间接实现的改进响应之间存在折衷。向表中添加多个索引可能会产生类似的影响。

        【讨论】:

          【解决方案4】:

          来自here:-

          具有非键列的索引可以显着改善查询 查询中的所有列都包含在索引中时的性能 作为键或非键列。实现了性能提升 因为查询优化器可以定位其中的所有列值 索引;未访问表或聚集索引数据导致 更少的磁盘 I/O 操作。

          【讨论】:

            猜你喜欢
            • 2018-06-09
            • 1970-01-01
            • 1970-01-01
            • 2013-12-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多