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

Posted

技术标签:

【中文标题】PostgreSQL 中的分组限制:显示每个组的前 N ​​行,但仅当这些行中的第一行等于特定数据时【英文标题】:Grouped LIMIT in PostgreSQL: show the first N rows for each group, BUT only if the first of those row equals specific data 【发布时间】:2021-02-27 12:27:05 【问题描述】:

考虑下表:

SELECT * FROM report_raw_data;
ts         | d_stamp    | id_mod | value
-----------+------------+--------+------
1605450647 | 2020-11-15 | 1      | 60
1605464634 | 2020-11-15 | 2      | 54
1605382126 | 2020-11-14 | 1      | 40
1605362085 | 2020-11-14 | 3      | 33
1605355089 | 2020-11-13 | 1      | 60
1605202153 | 2020-11-12 | 2      | 30

我需要的是获取按每个 id_mod 的 ts 排序的前两行,但前提是 d_stamp 是当前日期(在本例中为 2020-11-15)。

到目前为止,我已经设法让每个 id_mod 的前两行按 ts 排序,但我很难找到唯一的当前日期 2020-11-15。

这是我的错误结果尝试:

SELECT * FROM (SELECT ROW_NUMBER() OVER (PARTITION BY id_mod ORDER BY ts DESC) AS r,t.* FROM 
report_raw_data t) x WHERE x.r <= 2;

ts         | d_stamp    | id_mod | value
-----------+------------+--------+------
1605450647 | 2020-11-15 | 1      | 60
1605382126 | 2020-11-14 | 1      | 40
1605464634 | 2020-11-15 | 2      | 54
1605202153 | 2020-11-12 | 2      | 30
1605362085 | 2020-11-14 | 3      | 33

如果我在查询中使用 WHERE = '2020-11-15',我最终将只获得我需要的那些记录(因此没有第二行)。

这是我想要得到的(忽略 id_mod 编号 3),因为它是第一行不是从 2020-11-15 开始的:

ts         | d_stamp    | id_mod | value
-----------+------------+--------+------
1605450647 | 2020-11-15 | 1      | 60
1605382126 | 2020-11-14 | 1      | 40
1605464634 | 2020-11-15 | 2      | 54
1605202153 | 2020-11-12 | 2      | 30

还有一点需要注意:我需要能够在查询中使用 LIMIT 和 OFFSET 才能对前端的结果进行分页。

【问题讨论】:

【参考方案1】:

从您当前的查询开始,一个简单的方法是在子查询中使用一个窗口MAX() 来根据id_mod 恢复最新的ts。然后,您可以将其用于外部查询中的其他过滤。

SELECT * 
FROM (
    SELECT t.*,
        ROW_NUMBER() OVER (PARTITION BY id_mod ORDER BY ts DESC) AS rn,
        MAX(ts)      OVER(PARTITION BY id_mod) max_ts
    FROM report_raw_data t
) x 
WHERE rn <= 2 and max_ts = current_date;

【讨论】:

@xyz83242:你不接受这个答案有什么原因吗?【参考方案2】:

假设您没有未来的数据,我建议:

SELECT rdr.*
FROM (SELECT rdr.*,
             ROW_NUMBER() OVER (PARTITION BY id_mod ORDER BY ts DESC) AS seqnum
      FROM report_raw_data rdr
      WHERE d_stamp = current_date
     ) rdr
WHERE seqnum <= 2;

基于子查询中的时间进行过滤应该会显着提高性能。为了获得最佳性能,您需要在(d_stamp, id_mod, ts desc) 上建立索引。

【讨论】:

以上是关于PostgreSQL 中的分组限制:显示每个组的前 N ​​行,但仅当这些行中的第一行等于特定数据时的主要内容,如果未能解决你的问题,请参考以下文章

mysql中分组之后取每个组的前三个

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

获取每组的前 n 个结果 [重复]

如何限制jasper报告不要在每个组的新页面上启动?

oracle开展分组后,取出每组的前几条数据

在具有多个数字列的数据框中显示每个组的前 5 行