多列索引和ORDER BY

Cod*_*ode 6 postgresql indexing sql-order-by

PostgreSQL 文档指出,如果我们... ORDER BY x ASC, y DESC对具有索引的表运行查询... (x ASC, y ASC),则无法使用索引,因为方向不匹配.

  1. 这是否意味着此索引完全无用,或者数据库引擎是否可以使用索引来订购x ASC部件(然后手动对y DESC部件进行排序)?
  2. 如果我们运行查询... WHERE x = 999 ORDER BY y DESC,可以使用此索引吗?

Erw*_*ter 5

回答问题 1。

Postgres 12 或以上

,不能使用索引,就像手册建议的那样。您可以通过在任何表上创建这样的索引来验证,然后仅针对测试会话:

SET enable_seqscan = OFF;
Run Code Online (Sandbox Code Playgroud)

然后:

EXPLAIN
SELECT * FROM tbl ORDER BY ORDER BY x, y DESC;
Run Code Online (Sandbox Code Playgroud)

现在,如果可以以任何方式使用索引,那就是。但是您仍然会看到顺序扫描。

极端情况例外:如果仅索引扫描是可能的,并且该索引远小于表,则可能仍会使用该索引。但是行必须从头开始排序。

有关的:

Postgres 13 或更新版本

Postgres的13加入“增量排序”,其可以与GUC设置控制enable_incremental_sorton通过默认。发行说明:

如果已知中间查询结果按所需排序顺序的一个或多个前导键进行排序,则可以仅考虑剩余的键进行附加排序,如果行按具有相同前导键的批次进行排序。

版本 13.1 和 13.2 已修复边角问题。所以 - 一如既往 - 一定要运行最新的点发布。

现在,可以使用索引了。你会在EXPLAIN计划中看到这样的事情:

SET enable_seqscan = OFF;
Run Code Online (Sandbox Code Playgroud)

它不如具有匹配(切换)排序顺序的索引有效,其中可以直接从索引中读取容易排序的行(根本没有排序步骤)。但它可以是一个巨大的改进,尤其是对于一个小的LIMIT,Postgres 必须在历史上对所有行进行排序。现在它可以查看每个(一组)前导列,仅对这些列进行排序,并LIMIT在满足时立即停止。

回答问题 2。

是的,索引非常适合。

(即使索引有y ASC也可以工作。它可以向后扫描。NULL在这种情况下,只有放置是一个缺点。)

当然,如果x = 999是一个稳定的谓词(它总是999我们感兴趣的)并且不止几行有不同的x,那么部分索引会更有效:

CREATE INDEX ON tbl (y DESC) WHERE x = 999;
Run Code Online (Sandbox Code Playgroud)

db<>在这里小提琴- Postgres 10

db<>fiddle here - Postgres 13(第二个演示现在使用增量排序)