多列索引和 ORDER BY
Multi-column indexes and ORDER BY
PostgreSQL
documentation 声明如果我们 运行 对具有索引 ... (x ASC, y ASC)
的 table 进行查询 ... ORDER BY x ASC, y DESC
,则索引无法使用,因为方向不匹配。
- 这是否意味着这个索引完全没有用,或者数据库引擎可以使用索引对
x ASC
部分进行排序(然后手动对y DESC
部分进行排序)?
- 如果我们运行一个查询
... WHERE x = 999 ORDER BY y DESC
,这个索引可以用吗?
问题 1 的答案。
Postgres 12 或更早版本
否,索引无法使用,如手册所述。您可以通过在任何 table 上创建这样的索引来验证,然后仅针对测试会话:
SET enable_seqscan = OFF;
然后:
EXPLAIN
SELECT * FROM tbl ORDER BY ORDER BY x, y DESC;
现在,如果索引可以任何 方式使用,那就可以了。但是您仍然会看到顺序扫描。
极端情况例外:如果 index-only scan 是可能的,如果索引比 table 小得多,则可能仍会使用该索引。但是必须从头开始对行进行排序。
相关:
Postgres 13 或更新版本
Postgres 13 添加了“增量排序”,可以通过 GUC 设置 enable_incremental_sort
控制,默认为 on
。 The release notes:
If an intermediate query result is known to be sorted by one or more
leading keys of a required sort ordering, the additional sorting can
be done considering only the remaining keys, if the rows are sorted in
batches that have equal leading keys.
13.1 和 13.2 版已修复极端情况问题。所以 - 一如既往 - 请务必 运行 最新版本。
现在可以使用索引了。您会在 EXPLAIN
计划中看到类似这样的内容:
Sort Key: x, y DESC
Presorted Key: x
它不如具有匹配(切换)排序顺序的索引高效,后者可以直接从索引中读取易于排序的行(根本没有排序步骤)。但这可能是一个 巨大的 改进,尤其是对于 LIMIT
的小改进,其中 Postgres 必须根据历史对所有行进行排序。现在它可以查看每个(一组)前导列,仅对这些列进行排序,并在满足 LIMIT
后立即停止。
问题 2 的答案。
是的,索引完全符合。
(如果索引有 y ASC
也可以。它可以向后扫描。在这种情况下只有 NULL
位置是缺点。)
当然,如果 x = 999
是一个 stable 谓词(它总是 999
我们感兴趣)并且不止几行有不同的 x
,那么 partial index 会更有效率:
CREATE INDEX ON tbl (y DESC) WHERE x = 999;
db<>fiddle here - Postgres 10
db<>fiddle here - Postgres 13(第二个演示现在使用增量排序)
PostgreSQL
documentation 声明如果我们 运行 对具有索引 ... (x ASC, y ASC)
的 table 进行查询 ... ORDER BY x ASC, y DESC
,则索引无法使用,因为方向不匹配。
- 这是否意味着这个索引完全没有用,或者数据库引擎可以使用索引对
x ASC
部分进行排序(然后手动对y DESC
部分进行排序)? - 如果我们运行一个查询
... WHERE x = 999 ORDER BY y DESC
,这个索引可以用吗?
问题 1 的答案。
Postgres 12 或更早版本
否,索引无法使用,如手册所述。您可以通过在任何 table 上创建这样的索引来验证,然后仅针对测试会话:
SET enable_seqscan = OFF;
然后:
EXPLAIN
SELECT * FROM tbl ORDER BY ORDER BY x, y DESC;
现在,如果索引可以任何 方式使用,那就可以了。但是您仍然会看到顺序扫描。
极端情况例外:如果 index-only scan 是可能的,如果索引比 table 小得多,则可能仍会使用该索引。但是必须从头开始对行进行排序。
相关:
Postgres 13 或更新版本
Postgres 13 添加了“增量排序”,可以通过 GUC 设置 enable_incremental_sort
控制,默认为 on
。 The release notes:
If an intermediate query result is known to be sorted by one or more leading keys of a required sort ordering, the additional sorting can be done considering only the remaining keys, if the rows are sorted in batches that have equal leading keys.
13.1 和 13.2 版已修复极端情况问题。所以 - 一如既往 - 请务必 运行 最新版本。
现在可以使用索引了。您会在 EXPLAIN
计划中看到类似这样的内容:
Sort Key: x, y DESC
Presorted Key: x
它不如具有匹配(切换)排序顺序的索引高效,后者可以直接从索引中读取易于排序的行(根本没有排序步骤)。但这可能是一个 巨大的 改进,尤其是对于 LIMIT
的小改进,其中 Postgres 必须根据历史对所有行进行排序。现在它可以查看每个(一组)前导列,仅对这些列进行排序,并在满足 LIMIT
后立即停止。
问题 2 的答案。
是的,索引完全符合。
(如果索引有 y ASC
也可以。它可以向后扫描。在这种情况下只有 NULL
位置是缺点。)
当然,如果 x = 999
是一个 stable 谓词(它总是 999
我们感兴趣)并且不止几行有不同的 x
,那么 partial index 会更有效率:
CREATE INDEX ON tbl (y DESC) WHERE x = 999;
db<>fiddle here - Postgres 10
db<>fiddle here - Postgres 13(第二个演示现在使用增量排序)