为啥 row_number() 比使用偏移快?
Posted
技术标签:
【中文标题】为啥 row_number() 比使用偏移快?【英文标题】:Why is row_number() faster than using offset?为什么 row_number() 比使用偏移快? 【发布时间】:2014-06-16 19:36:44 【问题描述】:在 PostgreSQL 9.3 中尝试使用窗口函数时,我遇到了一个相当有趣的案例。与OFFSET vs. ROW_NUMBER() 上的答案直接矛盾的是,我发现窗口函数实际上快 比OFFSET。
使用偏移量,大约需要 2500 毫秒:
select part_no, description
from inventory
order by part_no
limit 1000 offset 400000
使用 row_number(),大约需要 450 毫秒:
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】:您的比较不完全有效。你需要使用:
row_number() OVER (<b>ORDER BY part_no</b>)
获得等效的结果。而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 进行测试?在过去的几年里有很多改进......
【讨论】:
我把 ORDER BY 移到了 OVER 子句中,似乎对性能影响不大。有趣的是,添加外部 ORDER BY 导致查询再次运行非常缓慢(>5s),但似乎根本没有改变结果的顺序。似乎外部 ORDER BY 的存在导致规划器显示两个排序步骤,其中前一种情况同时省略(尽管必须在 WindowAgg 中隐含一个。以上是关于为啥 row_number() 比使用偏移快?的主要内容,如果未能解决你的问题,请参考以下文章