假设我在 Postgres (11.3) 中有一个简单的表:
create table posts
(
id serial not null,
created_at timestamp(0)
constraint posts_pkey
primary key (id)
);
Run Code Online (Sandbox Code Playgroud)
如果用户请求 id=5869,我需要能够在按列排序的查询中返回该行之前的 N 行和之后的 N 行created_at。如果我们能够假设 越大id,则 越大created_at,我们可以做一些相对简单的事情,如下所示:
(select * from posts where id < 5869 order by id limit 10)
union all
(select * from posts where id >= 5869 order by id limit 11);
Run Code Online (Sandbox Code Playgroud)
但是,我无法假设更高的 id 是最近创建的,我想知道在这种情况下检索该数据的最佳方法是什么。此方法有效,但在 100k 行数据集上速度非常慢:
WITH
boundaries AS (
SELECT *,
row_number() OVER (ORDER BY created_at DESC) AS rownum
FROM posts
),
target_boundary AS (
SELECT *
FROM boundaries
WHERE boundaries.id = 5869
)
SELECT posts.*, boundaries.rownum
FROM posts
LEFT JOIN boundaries ON posts.id = boundaries.id
JOIN target_boundary ON boundaries.rownum BETWEEN target_boundary.rownum - 10 AND target_boundary.rownum + 10
Run Code Online (Sandbox Code Playgroud)
运行整个过程需要 800 毫秒以上,这对于如此小的数据集来说太慢了。
lead()我还尝试了使用and的上述变体lag(),但效率更低。
有没有更好的方法来执行此查询?我在 Postgres 中是否缺少一个可以处理它的窗口函数?
使用以下力量UNION ALL:
WITH init AS (
SELECT created_at
FROM posts
WHERE id = 5869
)
(
(SELECT posts.*
FROM posts
CROSS JOIN init
WHERE posts.created_at <= init.created_at
ORDER BY posts.created_at DESC
LIMIT 11)
UNION ALL
(SELECT posts.*
FROM posts
CROSS JOIN init
WHERE posts.created_at > init.created_at
ORDER BY posts.created_at
LIMIT 10)
);
Run Code Online (Sandbox Code Playgroud)
此查询假设 中没有重复项created_at。
为了获得良好的性能,您需要索引id(带有主键)和created_at。
如果您需要对结果进行排序,请使用我的查询作为子选择并添加一个ORDER BY.
| 归档时间: |
|
| 查看次数: |
4360 次 |
| 最近记录: |