PostgreSQL 查找按日期分组的前 N 行
Posted
技术标签:
【中文标题】PostgreSQL 查找按日期分组的前 N 行【英文标题】:PostgreSQL find top N rows grouped by date 【发布时间】:2014-09-03 03:10:27 【问题描述】:我正在开发一个典型的博客应用程序,并有一个返回以下数据的视图:
| post_id | title | publish_on | tag_id | tag_name |
| 1 | Why is Postgres awesome | 2014-09-02 | 1 | tech |
| 1 | Why is Postgres awesome | 2014-09-02 | 2 | postgres |
| 2 | How to ask a question on *** | 2014-09-10 | 1 | tech |
| 2 | How to ask a question on *** | 2014-09-10 | 2 | postgres |
| 2 | How to ask a question on *** | 2014-09-10 | 3 | guide |
| 3 | This is a draft | null | null | null |
| 4 | This is something else without a tag | 2014-10-10 | null | null |
| 5 | This question is also published on 9/2 | 2014-09-02 | null | null |
| 6 | And so is this | 2014-09-02 | 1 | tech |
| 7 | But this one is on 9/10 | 2014-09-10 | 3 | guide|
| 8 | This is on 10/10 | 2014-10-10 | null | null |
| 9 | And so is this | 2014-10-10 | 2 | postgres |
| 10| This is another draft | null | null | null |
我希望按 publish_on 日期对帖子进行分组,然后为每个存储桶选择前 3 个帖子(这将显示在仪表板中,以便用户可以知道今天、下周某个时间以及稍后发布的帖子) 现在我尝试了 these solutions 使用类似的东西:
ROW_NUMBER() OVER (PARTITION BY publish_on ORDER BY publish_on DESC)
但由于有多个标签,行可能会重复,因此这些查询会失败。我还尝试了各种 PARTION BY
标准的组合,但我想我对它们的理解不够好,无法让它发挥作用。
任何帮助/指针表示赞赏!
更新:预期输出
对于每个 publish_on 日期,我希望获得 N (3) 篇预计在该日期发布的帖子。
| 1 | Why is Postgres awesome | 2014-09-02 | 1 | tech |
| 1 | Why is Postgres awesome | 2014-09-02 | 2 | postgres |
| 5 | This question is also published on 9/2 | 2014-09-02 | null | null |
| 6 | And so is this | 2014-09-02 | 1 | tech |
| 2 | How to ask a question on *** | 2014-09-10 | 1 | tech |
| 2 | How to ask a question on *** | 2014-09-10 | 2 | postgres |
| 2 | How to ask a question on *** | 2014-09-10 | 3 | guide |
| 7 | But this one is on 9/10 | 2014-09-10 | 3 | guide|
| 4 | This is something else without a tag | 2014-10-10 | null | null |
| 8 | This is on 10/10 | 2014-10-10 | null | null |
| 9 | And so is this | 2014-10-10 | 2 | postgres |
| 3 | This is a draft | null | null | null |
| 10| This is another draft | null | null | null |
希望这能让问题更清楚易懂。
【问题讨论】:
tag_id 和 tag_name 是否相关?我假设是这样,但 tag_id 有相关的 tag_name 'postgres' 和 'guide'。 @simo.3792095 抱歉,它们是相关的。将更新表格以反映这一点。谢谢! 我假设 post_id 的 4、8 和 10 将在 10 月 10 日分组显示。您目前在 9 月 10 日之前显示 post_id 4。 啊,你说得对,4、8、9应该分开分组! 是的 4,8 & 9 - 不是我说的 4, 8 & 10。 ;-0 【参考方案1】:这就是你要找的吗? SQL Fiddle
SELECT *
FROM (SELECT ROW_NUMBER() OVER (PARTITION BY tag_name order by publish_on DESC) AS r,
t.*
from blog t ) x
where x.r <= 3
解释和问题:
我假设“每个存储桶”是指 tag_name(或 tag_id)。然后,您只需要“每个存储桶”中的 3 个最新帖子。如果一个帖子被多次标记,那么您希望如何处理它们 - 每个标签出现一次 - 或每个结果集只出现一次?
编辑
这会按您的预期显示结果。 SQL Fiddle for this here.
SELECT DISTINCT x.Post_id, y.title, x.Publish_on, y.tag_id, y.tag_name
FROM blog y
INNER JOIN (SELECT ROW_NUMBER() OVER (PARTITION BY publish_on order by publish_on DESC) AS r,
t.post_id, t.publish_on
from (SELECT DISTINCT s.post_id, s.publish_on
FROM blog s) t
) x ON x.post_id = y.post_id
where x.r <= 3
ORDER BY x.publish_on
增加复杂性的主要问题是表结构没有标准化。这实际上应该是 3 个表,这样描述和日期就不会在不同的行中重复,即
CREATE TABLE blog
(post_id int not null,
title varchar(50) not null,
publish_on date)
CREATE TABLE blog_tag
(post_id int not null,
tag_ig int not null)
CREATE TABLE tag
(tag_id int not null,
tag_name varchar(10) not null)
那么SQL可以换成see full SQL Fidle for this here.
SELECT x.Post_id, x.title, x.Publish_on, t.tag_id, t.tag_name
FROM (SELECT ROW_NUMBER() OVER (PARTITION BY publish_on order by publish_on DESC) AS r,
b.*
from blog b) x
LEFT JOIN blog_tag bt ON bt.post_id = x.post_id
LEFT JOIN tag t ON t.tag_id = bt.tag_id
WHERE x.r <= 3
ORDER BY x.publish_on, x.post_id, t.tag_id
【讨论】:
我已经用预期的输出更新了这个问题。通过存储桶,我的意思是“日期分组”。你能再看看这个问题吗? 效果很好!这些表实际上是规范化的,示例数据来自一个视图。您发布的两个选项都有效,但由于我可以控制视图,因此我将相应地对其进行修改以使用选项 2。谢谢!以上是关于PostgreSQL 查找按日期分组的前 N 行的主要内容,如果未能解决你的问题,请参考以下文章
PostgreSQL 中的分组限制:显示每个组的前 N 行?
PostgreSQL 中的分组限制:显示每个组的前 N 行,但仅当这些行中的第一行等于特定数据时