MSSQL里复合索引和包含索引到底差在哪儿,啥时候用哪个更合适呢
- 问答
- 2025-12-28 15:31:36
- 1
要理解这两个概念的区别,我们可以用一个非常生活化的比喻:查字典。
复合索引就像是一本按照“部首”和“笔画数”两个字段来排序的字典,如果你想找某个字,你必须先知道它的部首,然后在那个部首里,再按照笔画数去查找,这里的关键点是,“部首”和“笔画数”不仅是查找的依据,也共同决定了每个字在字典中的排序位置,如果你只知道笔画数,而不知道部首,这本字典用起来就会非常困难,甚至需要从头翻到尾,这就是所谓的“索引失效”。
包含索引则像是一本普通的按拼音排序的字典,但在每个字的解释后面,额外附上了这个字的“英文翻译”和“词组示例”,当你用拼音查到这个字时,你就能直接看到所有这些额外信息,不需要再翻到书的其他地方,这里,拼音是查找的依据,而“英文翻译”和“词组示例”只是被“包含”在这里的附加信息,它们并不参与字典的排序,你不可能通过“英文翻译”来查找这个字。
基于这个比喻,我们来详细拆解它们的区别。
根本区别:索引键的角色不同
这个区别是核心,来源于微软官方文档对索引结构的定义。
-
复合索引(Compound Index): 由多个列共同组成索引的键列,这些键列都参与B-tree索引结构的排序和查找,就像字典按“部首-笔画”排序一样,数据行在索引中的顺序是先按第一列排序,第一列相同时再按第二列排序,以此类推,查询条件必须涉及到复合索引的最左前缀(比如使用了索引的第一列),索引才能被高效使用。
-
包含索引(Covering Index with INCLUDED Columns): 只有一部分列是键列,参与索引结构的排序和查找,另一部分列作为包含列,它们的数据只是被“挂”在索引的叶子节点上,不参与排序,包含列的存在,仅仅是为了让索引能够“覆盖”更多的查询,避免再去查找原始数据表(即避免键查找操作)。
性能与限制的差异
-
对查询的适用性:
- 复合索引:最适合用于带有WHERE条件过滤和ORDER BY排序的查询,尤其是当过滤和排序的列顺序与索引列顺序一致时,效率极高,如果查询条件跳过了索引的最左列(比如复合索引是(A, B),但查询只条件过滤B),那么这个复合索引基本上就无效了。
- 包含索引:主要目标是实现“索引覆盖扫描”,即查询所需要的所有数据都能从索引中直接获取,而无需回表,它本身对WHERE条件中的过滤帮助有限,过滤能力完全依赖于它的键列,它的巨大优势在于,如果查询只需要键列和包含列,数据库引擎只需读取索引页,速度极快。
-
索引大小和维护开销:
- 复合索引:因为所有键列都参与排序,所以索引键的长度直接影响索引树的大小和深度,如果键列很多或者有很宽的列(如NVARCHAR(MAX)),索引会变得非常庞大,维护起来(增删改操作)开销也更大。
- 包含索引:更具灵活性,由于包含列不参与排序,所以即使包含很宽的列,对索引树的主体结构大小影响也相对较小,它只在叶子节点存储这些包含列的值,因此可以以一种更经济的方式将宽列添加到索引中,以减少键查找,微软文档也建议,当有宽类型的列需要被包含时,应优先使用包含列而非将其作为键列。
-
索引键的限制:
- 复合索引:所有键列必须遵守索引键的限制,比如所有键列总长度不能超过900字节,且不能包含不允许作为索引键的数据类型(如
text,ntext,image)。 - 包含索引:键列依然要遵守900字节的限制。包含列不受此限制!包含列可以是任何允许的数据类型(包括LOB类型如
varchar(max)),只要所有键列的大小符合限制即可,这是包含索引一个非常强大的优势。
- 复合索引:所有键列必须遵守索引键的限制,比如所有键列总长度不能超过900字节,且不能包含不允许作为索引键的数据类型(如
什么时候用哪个更合适?
这个选择没有绝对的答案,取决于你的具体查询模式。
优先考虑使用复合索引的情况:
- 当你的查询条件(WHERE子句)经常同时使用多个列进行过滤时。 你经常按“城市”和“姓氏”查人,那么建立(城市, 姓氏)的复合索引就非常高效。
- 当你的查询需要按多个列进行排序(ORDER BY),并且排序顺序与索引列顺序一致时。 你需要经常按“订单日期”降序、再按“订单金额”降序排序,那么建立(订单日期 DESC, 订单金额 DESC)的复合索引可以直接利用索引排序,避免昂贵的排序操作。
优先考虑使用包含索引(即在复合索引的基础上添加包含列)的情况:
- *当你的查询只需要少数几列做条件过滤,但需要返回很多其他列时(即SELECT 或 SELECT 多列)。** 这是最经典的场景,你90%的查询都是根据“用户名”(键列)查找,但需要返回用户的“邮箱”、“电话”、“创建时间”等信息,那么建立一个(用户名)为键列,包含(邮箱, 电话, 创建时间)的索引,可以使这个高频查询性能达到极致,因为引擎只需扫描索引。
- 当你想包含的列是大型对象(如VARCHAR(MAX))或宽度很大的列时。 由于包含列不受900字节限制,且不增加索引树的排序负担,这是唯一可行的方案。
- 当你的过滤条件相对固定,但需要覆盖多个不同的查询场景时。 你可以设计一个以最常用过滤条件为键列的索引,然后将其他各种查询可能需要的列都作为包含列加入,这样一个索引可能就能覆盖公司内部好几个报表查询,实现“一劳永逸”。
组合使用:
在实际应用中,复合索引和包含索引常常是结合使用的,你会创建一个由多个列组成的复合索引作为键列(用于高效的过滤和排序),然后再把查询中需要返回的其他列作为包含列添加进去,一个针对报表查询的优化索引可能是:CREATE INDEX IX_Report_Cover ON Orders (OrderDate, CustomerID) INCLUDE (TotalAmount, ProductName, Status),这个索引可以高效地处理按日期和客户ID过滤,并直接返回金额、产品名和状态的查询。
选择的关键在于分析你的SQL语句:用于过滤和排序的列,适合做键列(构成复合索引);仅用于返回结果的列,适合做包含列。 理解了这个原则,就能在合适的场景做出合适的选择。

本文由符海莹于2025-12-28发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/70107.html
