为什么row_number()比使用offset更快?

时间:2014-06-16 19:36:44

标签: sql postgresql query-optimization

在使用PostgreSQL 9.3中的窗口函数进行一些实验时,我遇到了一个相当有趣的案例。与OFFSET vs. ROW_NUMBER()上的答案直接矛盾,我发现窗口函数实际上比OFFSET 更快

使用偏移,需要~2500ms:

select part_no, description
from inventory
order by part_no
limit 1000 offset 400000

使用row_number()需要~450ms:

select *
from (select part_no, description, row_number() OVER () 
    from inventory
    order by part_no) AS ss
where row_number >= 400001
limit 1000

这个(新分析的)表有大约450,000行,part_no被索引。 EXPLAIN表示正在row_number()情况下执行索引扫描,并在OFFSET情况下执行顺序扫描。

我尝试过不同的OFFSET,row_number()和不同的偏移量大小以及索引与未索引的排序顺序的组合。所有时间都是几次运行的近似平均值(查询时间通常非常一致。)

            -------indexed-------    ------unindexed------
offset by   OFFSET   row_number()    OFFSET   row_number()
==========================================================
400000      2500ms          450ms     500ms          650ms
40000         80ms           60ms     850ms          650ms
4000          30ms           30ms     390ms          650ms

我想这里真正的问题是;什么是查询规划器在这两种情况下做的不同,我怎样才能让它做出更好的选择(特别是在大偏移+索引列的情况下)?

1 个答案:

答案 0 :(得分:3)

您的比较不完全有效。你需要使用:

row_number() OVER (ORDER BY part_no)

获得相同的结果。并且ORDER BY需要移动到外部查询。所以:

SELECT part_no, description
FROM  (
   SELECT part_no, description, row_number() OVER (ORDER BY part_no) AS rn
   FROM   inventory) AS ss
WHERE  rn > 400000
ORDER  BY rn
LIMIT  1000;

或者:

SELECT part_no, description
FROM  (
   SELECT part_no, description, row_number() OVER (ORDER BY part_no) AS rn
   FROM   inventory) AS ss
WHERE  rn BETWEEN 400000 AND 401000
ORDER  BY rn;

此外,您所指的比较是4年,并且尚未声明Postgres的版本。我假设您正在测试最新的9.3?过去几年有很多改进......