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 ​​行的主要内容,如果未能解决你的问题,请参考以下文章

选择按日期排序的每组的前 N ​​行

PostgreSQL 中的分组限制:显示每个组的前 N ​​行?

PostgreSQL 中的分组限制:显示每个组的前 N ​​行,但仅当这些行中的第一行等于特定数据时

sql数据库怎么实现分组并取每组的前1条语句,按日期排序?

如何按 ID 分组并查找日期中的空白以确定 Alteryx 中的开始和结束日期?

如何在另一列中按条件分组的列中查找下一个日期?