sql - 优化 PostgreSQL 中的窗口函数以使用索引

标签 sql postgresql indexing greatest-n-per-group window-functions

我在 PostgreSQL 9.2 数据库中有一个表,创建和填充如下:

CREATE TABLE foo( id integer, date date );

INSERT INTO foo
SELECT (id % 10) + 1, now() - (id % 50) * interval '1 day'
FROM generate_series(1, 100000) AS id;

现在,我需要找到所有对 (id, date),使得日期是具有相同 id 的所有对中最大的一个。该查询是众所周知的,通常使用名为 ROW_NUMBER()

的窗口函数
SELECT id, date
FROM (
    SELECT id, date, ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) rn
    FROM foo
) sbt
WHERE sbt.rn = 1;

现在,我询问该查询的计划,并发现 WindowAgg 节点需要先对表进行排序。

Subquery Scan on sbt  (cost=11116.32..14366.32 rows=500 width=8) (actual time=71.650..127.809 rows=10 loops=1)
  Filter: (sbt.rn = 1)
  Rows Removed by Filter: 99990
    ->  WindowAgg  (cost=11116.32..13116.32 rows=100000 width=8) (actual time=71.644..122.476 rows=100000 loops=1)
          ->  Sort  (cost=11116.32..11366.32 rows=100000 width=8) (actual time=71.637..92.081 rows=100000 loops=1)
                Sort Key: foo.id, foo.date
                Sort Method: external merge  Disk: 1752kB
                ->  Seq Scan on foo  (cost=0.00..1443.00 rows=100000 width=8) (actual time=0.006..6.138 rows=100000 loops=1)

正如预期的那样,排序占用了大部分查询执行时间,使用 indexes 肯定会有帮助。

所以我创建了 CREATE INDEX ON foo(id, date) 并期望现在它将使用索引。但事实并非如此。我得到了与 external merge 相同的计划,甚至关闭 sequential scan 也没有用。我刚刚结束 Bitmap Index Scan

->  Sort  (cost=12745.58..12995.58 rows=100000 width=8) (actual time=69.247..90.003 rows=100000 loops=1)
      Sort Key: foo.id, foo.date
      Sort Method: external merge  Disk: 1752kB
      ->  Bitmap Heap Scan on foo  (cost=1629.26..3072.26 rows=100000 width=8) (actual time=5.359..12.639 rows=100000 loops=1)
            ->  Bitmap Index Scan on foo_id_date_idx  (cost=0.00..1604.26 rows=100000 width=0) (actual time=5.299..5.299 rows=100000 loops=1)

问题
WindowAgg 可以使用索引进行排序吗?我认为它不能但不明白为什么...... GroupAggreagte 可以并且它提供了良好的性能改进。

最佳答案

要匹配您创建的索引:

CREATE INDEX ON foo(id, date)

你必须这样做:

ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC <b>NULLS LAST</b>) 

这是 ASC 的完美逆序。

除此之外,你可以运行:

SELECT DISTINCT ON (id)
       id, date
FROM   foo
ORDER  BY id, date DESC NULLS LAST;

但这可能不是您想问的。无论哪种方式,我都会制作索引:

CREATE INDEX ON foo(id, date DESC NULLS LAST)

因此 max(date) 是每个 id 的第一个索引条目。 相关:

关于sql - 优化 PostgreSQL 中的窗口函数以使用索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33118186/

相关文章:

sql - 按列 ASC 排序,但首先是 NULL 值?

Java JDBC 与 Heroku 的连接

mysql - SQL:具有特定值的一行中有多少行

sql - where 子句中的日期时间

mysql - 安装 Xampp 和 MySql 有什么区别?

sql - 如何改进此 SQL 查询?

python - 如何访问另一个列表中每个列表的第 n 个元素?

indexing - 在SQLite中从索引创建显式索引列

LIKE 的 MySQL 性能(无通配符)与 =

MySQL/MariaDB - 按内部子查询排序