代码之家  ›  专栏  ›  技术社区  ›  Cory

为什么在创建索引时使用INCLUDE子句?

  •  384
  • Cory  · 技术社区  · 17 年前

    在准备70-433考试时,我注意到你可以通过以下两种方法之一创建覆盖索引。

    CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)
    

    --或--

    CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)
    

    包括条款对我来说是新的。为什么要使用它?在决定是否创建包含或不包含INCLUDE子句的覆盖索引时,您会建议什么准则?

    7 回复  |  直到 13 年前
        1
  •  396
  •   JonH    5 年前

    WHERE/JOIN/GROUP BY/ORDER BY ,但仅在中的列列表中 SELECT 子句是您使用的地方 INCLUDE .

    这个 包括 这使得索引更小,因为它不是树的一部分

    INCLUDE columns 不是索引中的键列,因此它们没有顺序。 这意味着它对于我上面提到的谓词、排序等不是很有用。但是, 也许 如果在键列的几行中有剩余查找,则此选项非常有用

    Another MSDN article with a worked example

        2
  •  231
  •   HaveNoDisplayName    11 年前

    您可以使用INCLUDE将一个或多个列添加到非聚集索引的叶级,如果这样做可以“覆盖”查询的话。

    假设您需要查询员工ID、部门ID和姓氏。

    SELECT EmployeeID, DepartmentID, LastName
    FROM Employee
    WHERE DepartmentID = 5
    

    如果您碰巧在(EmployeeID,DepartmentID)上有一个非聚集索引,那么一旦您找到给定部门的员工,您现在必须执行“书签查找”以获取实际的完整员工记录,只需获取lastname列。如果你发现有很多员工,那么这在绩效方面可能会非常昂贵。

    如果您已将该姓氏包括在索引中:

    CREATE NONCLUSTERED INDEX NC_EmpDep 
      ON Employee(EmployeeID, DepartmentID)
      INCLUDE (Lastname)
    

    然后,您需要的所有信息都可以在非聚集索引的叶级中获得。只需在非聚集索引中查找并查找给定部门的员工,您就拥有了所有必要的信息,并且不再需要在索引中查找每个员工的书签-->你节省了很多时间。

    显然,您不能在每个非聚集索引中包含每一列,但是如果您确实有查询缺少一个或两个要“覆盖”的列(并且经常使用),那么将这些列包含到合适的非聚集索引中会非常有帮助。

        3
  •  30
  •   kevinbatchcom Fredrik Solhaug    10 年前

    这一讨论忽略了一个重要的问题:问题不是“非关键列”是否最好包含为 指数 -列或作为 包括

    在索引中并不真正需要 ? (通常不属于where子句,但通常包含在selects中)。所以你的困境总是:

    1. 在id1、id2上使用索引。。。idN 单独地
    2. 在id1、id2上使用索引。。。idN 加上包括 col1,col2。。。科恩

    id1,id2。。。idN列通常用于限制和col1、col2。。。colN是经常选择的列,但通常 用于限制

    (将所有这些列作为索引键的一部分的选项总是愚蠢的(除非它们也在限制中使用)-因为即使“键”没有更改,也必须更新和排序索引,因此维护这些列的成本总是更高)。

    那么使用选项1或2?

    答:如果您的表很少更新(主要是插入/删除),那么使用include机制包含一些“热列”(通常在select中使用)相对便宜,但是 通常用于限制),因为插入/删除要求索引无论如何都要更新/排序,因此在已经更新索引的情况下存储一些额外列的额外开销很小。开销是用于在索引上存储冗余信息的额外内存和CPU。

    如果您认为添加列包含的列经常被更新(没有索引) 钥匙

    键(id1、id2…idN)中每个相同值的平均行数也可能很重要。

    请注意,如果列-作为 限制规定 : (基于对索引的限制)- 钥匙 -列)-然后SQL Server将列限制与索引(叶节点值)匹配,而不是围绕表本身进行昂贵的操作。

        4
  •  19
  •   onupdatecascade    17 年前

    基本索引列已排序,但包含的列未排序。这节省了维护索引的资源,同时仍然可以在包含的列中提供数据以覆盖查询。因此,如果您想涵盖查询,可以将搜索条件用于将行定位到索引的已排序列中,然后使用非搜索数据“包括”其他未排序列。这无疑有助于减少索引维护中的排序和碎片数量。

        5
  •  7
  •   mrdenny    17 年前

    原因(包括索引叶级的数据)已经很好地解释了。对此,您会有两次动摇的原因是,当您运行查询时,如果没有包含额外的列(SQL 2005中的新功能),SQL Server必须转到聚集索引以获取额外的列,这需要更多的时间,并且会给SQL Server服务、磁盘和内存增加更多的负载(具体来说是缓冲区缓存)当新的数据页加载到内存中时,可能会将其他更经常需要的数据推出缓冲区缓存。

        6
  •  6
  •   Nibor    12 年前

    这允许您在覆盖索引中包含此类列。我最近不得不这样做,以提供一个nHibernate生成的查询,该查询在SELECT中有很多列,并带有一个有用的索引。

        7
  •  5
  •   Markus Winand    7 年前

    INCLUDE 键列上方 如果您不需要在键中使用该列 是文档。这使得未来发展索引更加容易。

    考虑到你的例子:

    CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)
    

    如果查询如下所示,则该索引是最好的:

    SELECT col2, col3
      FROM MyTable
     WHERE col1 = ...
    

    当然,您不应该在 col2

    SELECT col2, col3
      FROM MyTable
     WHERE col1 = ...
       AND col2 = ...
    
    SELECT TOP 1 col2, col3
      FROM MyTable
     WHERE col1 = ...
     ORDER BY col2
    

    让我们假设这是 可乐 包括 子句,因为将它放在索引的树部分没有任何好处。

    您需要调整此查询:

    SELECT TOP 1 col2
      FROM MyTable
     WHERE col1 = ...
     ORDER BY another_col
    

    要优化该查询,请使用以下索引:

    CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)
    

    如果检查该表上已有哪些索引,则以前的索引可能仍然存在:

    现在你知道了 Col2 Col3 another_column 到索引关键部分的末尾(在 col1

    DROP INDEX idx1 ON MyTable;
    CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);
    

    该指数将变得更大,仍然存在一些风险,但与引入新指数相比,扩展现有指数通常更好。

    如果你想要一个没有 another_col Col1 .

    CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)
    

    如果你加上 之间 可乐 可乐

    还有其他的“好处” 与键列的比较 如果添加这些列只是为了避免从表中提取它们 . 然而,我认为文档方面是最重要的。

    回答你的问题:

    如果向索引中添加列的唯一目的是使该列在索引中可用,而无需访问表,请将其放入 包括 条款

    如果将列添加到索引键会带来其他好处(例如 order by 或者因为它可以缩小读取索引范围)将其添加到键中。

    您可以在此处阅读有关此问题的详细讨论:

    https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes

        8
  •  2
  •   mEmENT0m0RI    15 年前

    索引定义中内联的所有列的总大小都有限制。尽管如此,我从未创建过这么宽的索引。 一个例子是StoreID(其中StoreID是低选择性的,这意味着每个商店都与许多客户关联),然后是客户人口统计数据(LastName、FirstName、DOB): 如果您只是按此顺序(StoreID、LastName、FirstName、DOB)内联这些列,则只能高效地搜索您知道StoreID和LastName的客户。

    另一方面,在StoreID上定义索引并包含LastName、FirstName和DOB列,本质上可以让您在StoreID上执行两个seek-index谓词,然后在任何包含的列上执行seek谓词。这将允许您覆盖所有可能的搜索排列,只要它以StoreID开头。