可以在表的多个列上定义索引。例如,如果您有一个具有以下形式的表
CREATE TABLE test2 ( major int, minor int, name varchar );
(假设您将您的 /dev
目录存储在数据库中…),并且您经常执行如下查询
SELECT name FROM test2 WHERE major =constant
AND minor =constant
;
那么,对 major
和 minor
列一起定义一个索引可能是合适的,例如:
CREATE INDEX test2_mm_idx ON test2 (major, minor);
目前,只有 B-tree、GiST、GIN 和 BRIN 索引类型支持多键列索引。是否存在多个键列与是否可以向索引添加 INCLUDE
列是独立的。索引最多可以有 32 列,包括 INCLUDE
列。(构建 PostgreSQL 时可以更改此限制;请参阅文件 pg_config_manual.h
。)
多列 B-tree 索引可以用于涉及索引列的任何子集的查询条件,但当对最左边的列存在等值约束时,该索引效率最高。确切的规则是,最左边列上的等值约束,加上第一个没有等值约束的列上的任何不等值约束,都将始终用于限制需要扫描的索引部分。右侧列上的约束将在索引中进行检查,因此它们总是可以节省对实际表的访问,但它们不一定能减少需要扫描的索引部分。如果 B-tree 索引扫描可以有效应用跳跃扫描优化,那么它将在遍历索引时通过重复的索引搜索来应用每个列约束。这可以减少需要读取的索引部分,即使一个或多个列(在查询谓词中最不显著的索引列之前)缺乏常规的等值约束。跳跃扫描的工作原理是通过内部生成一个动态等值约束,该约束匹配索引列中的每个可能值(但仅在给定一个缺少查询谓词的等值约束的列,并且仅当生成的约束可以与查询谓词中的后续列约束一起使用时)。
例如,给定一个在 (x, y)
上的索引,以及查询条件 WHERE y = 7700
,B-tree 索引扫描可能会应用跳跃扫描优化。这通常发生在查询规划器预期对于每个可能的 N
值(或对于表中实际存储的每个 x
值)重复执行 WHERE x = N AND y = 7700
搜索是给定表中可用索引的最快方法时。这种方法通常仅在 x
的不同值非常少的情况下才会被采用,以至于规划器预期扫描将跳过大部分索引(因为其大部分叶子页面可能不包含相关的元组)。如果 x
的不同值很多,那么将需要扫描整个索引,因此在大多数情况下,规划器将优先选择顺序表扫描而不是使用索引。
跳跃扫描优化也可以选择性地应用,在至少包含一些有用查询谓词约束的 B-tree 扫描期间。例如,给定一个在 (a, b, c)
上的索引和查询条件 WHERE a = 5 AND b >= 42 AND c < 77
,索引可能需要从 a
= 5 和 b
= 42 的第一个条目扫描到 a
= 5 的最后一个条目。具有 c
>= 77 的索引条目永远不需要在表级别进行过滤,但跳过它们在索引内部是否有益可能有所不同。当发生跳跃时,扫描会启动一个新的索引搜索,从当前 a
= 5 和 b
= N 组的末尾(即,在索引中第一个元组 a = 5 AND b = N AND c >= 77
出现的位置)重新定位到下一组的开头(即,在索引中第一个元组 a = 5 AND b = N + 1
出现的位置)。
多列 GiST 索引可以用于涉及索引列的任何子集的查询条件。其他列上的条件会限制索引返回的条目,但第一列上的条件对于确定需要扫描多少索引非常重要。如果 GiST 索引的第一列只有少量不同的值,即使其他列有许多不同的值,该索引的有效性也会相对较低。
多列 GIN 索引可以用于涉及索引列的任何子集的查询条件。与 B-tree 或 GiST 不同,无论查询条件使用哪个(些)索引列,索引搜索的有效性都是相同的。
多列 BRIN 索引可以用于涉及索引列的任何子集的查询条件。与 GIN 类似,但与 B-tree 或 GiST 不同,无论查询条件使用哪个(些)索引列,索引搜索的有效性都是相同的。在单个表上拥有多个 BRIN 索引而不是一个多列 BRIN 索引的唯一原因是拥有不同的 pages_per_range
存储参数。
当然,每个列都必须使用适合索引类型的运算符;涉及其他运算符的子句将不被考虑。
应谨慎使用多列索引。在大多数情况下,单列索引就足够了,并且可以节省空间和时间。超过三个列的索引除非表的用法非常特殊,否则不太可能有帮助。有关不同索引配置的优缺点的讨论,请参阅 Section 11.5 和 Section 11.9。
如果您在文档中看到任何不正确、与您对特定功能的经验不符或需要进一步阐明的内容,请使用 此表单 来报告文档问题。