多列索引和 ORDER BY

Multi-column indexes and ORDER BY

PostgreSQL documentation 声明如果我们 运行 对具有索引 ... (x ASC, y ASC) 的 table 进行查询 ... ORDER BY x ASC, y DESC,则索引无法使用,因为方向不匹配。

  1. 这是否意味着这个索引完全没有用,或者数据库引擎可以使用索引对x ASC部分进行排序(然后手动对y DESC部分进行排序)?
  2. 如果我们运行一个查询... 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 控制,默认为 onThe 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(第二个演示现在使用增量排序)