优化 PostgreSQL 中的 window 函数以使用索引
Optimizing window function in PostgreSQL to use index
我在 PostgreSQL 9.2 数据库中有一个 table,创建和填充如下:
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()
的 window 函数
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
节点需要先对 table 进行排序。
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)
正如预期的那样,排序占用了大部分查询执行时间,使用 index
es 肯定会有帮助。
所以我创建了 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
s 可以并且它提供了良好的性能改进。
要匹配您创建的索引:
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
的第一个索引条目。
相关:
- PostgreSQL sort by datetime asc, null first?
- Why do NULL values come first when ordering DESC in a PostgreSQL query?
- Select first row in each GROUP BY group?
- Optimize groupwise maximum query
您可以将逻辑 "the date is a maximum one among all pairs with the same id" 重写为直接翻译:
SELECT id, date
FROM (
SELECT id, date, MAX(date) OVER (PARTITION BY id) as maxDate
FROM foo
) sbt
WHERE date = maxDate;
它与 ROW_NUMBER
并不完全相同,而是 RANK
,它可能 return 多行具有相同的 date
我在 PostgreSQL 9.2 数据库中有一个 table,创建和填充如下:
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
节点需要先对 table 进行排序。
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)
正如预期的那样,排序占用了大部分查询执行时间,使用 index
es 肯定会有帮助。
所以我创建了 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
s 可以并且它提供了良好的性能改进。
要匹配您创建的索引:
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
的第一个索引条目。
相关:
- PostgreSQL sort by datetime asc, null first?
- Why do NULL values come first when ordering DESC in a PostgreSQL query?
- Select first row in each GROUP BY group?
- Optimize groupwise maximum query
您可以将逻辑 "the date is a maximum one among all pairs with the same id" 重写为直接翻译:
SELECT id, date
FROM (
SELECT id, date, MAX(date) OVER (PARTITION BY id) as maxDate
FROM foo
) sbt
WHERE date = maxDate;
它与 ROW_NUMBER
并不完全相同,而是 RANK
,它可能 return 多行具有相同的 date