OTA*_*TAR 9 sql postgresql pagination sql-order-by postgresql-9.5
我有桌子
create table big_table (
id serial primary key,
-- other columns here
vote int
);
Run Code Online (Sandbox Code Playgroud)
这个表非常大,大约有7000万行,我需要查询:
SELECT * FROM big_table
ORDER BY vote [ASC|DESC], id [ASC|DESC]
OFFSET x LIMIT n -- I need this for pagination
Run Code Online (Sandbox Code Playgroud)
您可能知道,当x数字很大时,这样的查询非常慢.
为了性能优化,我添加了索引:
create index vote_order_asc on big_table (vote asc, id asc);
Run Code Online (Sandbox Code Playgroud)
和
create index vote_order_desc on big_table (vote desc, id desc);
Run Code Online (Sandbox Code Playgroud)
EXPLAIN显示上面的SELECT查询使用这些索引,但无论如何都有很大的偏移量.
如何OFFSET在大表中优化查询?也许PostgreSQL 9.5甚至更新版本都有一些功能?我搜索过但没找到任何东西.
Erw*_*ter 22
一个大OFFSET的总是很慢.Postgres必须订购所有行并计算可见的行数到您的偏移量.要直接跳过所有以前的行,您可以添加索引row_number到表(或创建MATERIALIZED VIEW包含说row_number)并使用WHERE row_number > x而不是OFFSET x.
但是,这种方法仅适用于只读(或大部分)数据.对可以同时更改的表数据实现相同的操作更具挑战性.您需要从准确定义所需行为开始.
我建议采用不同的分页方法:
SELECT *
FROM big_table
WHERE (vote, id) > (vote_x, id_x) -- ROW values
ORDER BY vote, id -- needs to be deterministic
LIMIT n;
Run Code Online (Sandbox Code Playgroud)
凡vote_x与id_x来自最后的排前一页(对于DESC和ASC).或者从第一个向后导航.
您已经拥有的索引支持比较行值 - 这是一个符合ANSI SQL的功能,但不是每个RDBMS都支持它.
CREATE INDEX vote_order_asc ON big_table (vote, id);
Run Code Online (Sandbox Code Playgroud)
或者降序排列:
SELECT *
FROM big_table
WHERE (vote, id) < (vote_x, id_x) -- ROW values
ORDER BY vote DESC, id DESC
LIMIT n;
Run Code Online (Sandbox Code Playgroud)
可以使用相同的索引.
我建议你声明你的专栏NOT NULL或熟悉NULLS FIRST|LAST构造:
特别注意两件事:
子句中的ROW值WHERE不能用分隔的成员字段替换.WHERE (vote, id) > (vote_x, id_x) 不能替换为:
WHERE vote >= vote_x
AND id > id_xRun Code Online (Sandbox Code Playgroud)
这将排除所有行id <= id_x,而我们只想为同一个投票而不是下一个投票.正确的翻译是:
WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
Run Code Online (Sandbox Code Playgroud)
...不能很好地与索引一起使用,并且对于更多列而言变得越来越复杂.
显然,对于单个列来说很简单.这是我在一开始就提到的特殊情况.
该技术不适用于混合方向,ORDER BY如:
ORDER BY vote ASC, id DESC
Run Code Online (Sandbox Code Playgroud)
至少我想不出一种有效实现这一点的通用方法.如果两列中的至少一列是数字类型,则可以使用具有反转值的功能索引(vote, (id * -1))- 并在以下表达式中使用相同的表达式ORDER BY:
ORDER BY vote ASC, (id * -1) ASC
Run Code Online (Sandbox Code Playgroud)有关:
请特别注意Markus Winand的演讲,我将其链接到: