按列值的差异对 SQL 中的数据进行分组

Posted

技术标签:

【中文标题】按列值的差异对 SQL 中的数据进行分组【英文标题】:Grouping data in SQL by difference in column values 【发布时间】:2018-02-12 06:34:32 【问题描述】:

我在 postgres 表的日志表中有以下数据:

    logid => int(自动递增) start_time => bigint(存储纪元值) inserted_value => int

以下是存储在表中的数据(其中实际开始时间不是列,只是以UTC格式以24小时格式显示start_time值)

logid   user_id    start_time       inserted_value       start time actual
 1      1          1518416562       15                   12-Feb-2018 06:22:42
 2      1          1518416622       8                    12-Feb-2018 06:23:42 
 3      1          1518417342       9                    12-Feb-2018 06:35:42 
 4      1          1518417402       12                   12-Feb-2018 06:36:42 
 5      1          1518417462       18                   12-Feb-2018 06:37:42
 6      1          1518418757       6                    12-Feb-2018 06:59:17 
 7      1          1518418808       11                   12-Feb-2018 07:00:08

我想根据 start_time 的差异对值进行分组和求和

对于上述数据,应分三组计算总和:

  user_id         sum
   1              15 + 8
   1              9 + 12 + 18
   1              6 + 11

因此,每组中的值有 1 分钟的差异。这个 1 可以被认为是任何 x 分钟的差异。

我也在尝试LAG 功能,但无法完全理解。我希望我能够解释我的问题。

【问题讨论】:

【参考方案1】:

您可以使用普通的group by 来实现您想要的。只需使属于同一分钟的所有start_time 值相等。例如

select user_id, start_time/60, sum(inserted_value)
from log_table
group by user_id, start_time/60

我假设您的 start_time 列包含表示毫秒的整数,因此 /60 将正确地将它们截断为分钟。如果值是浮点数,则应使用floor(start_time/60)

如果您还想选择分组时的可读日期,可以将to_timestamp((start_time/60)*60) 添加到选择列表中。

【讨论】:

start_time 以毫秒为单位存储纪元值,表示自 1970 年 1 月 1 日以来经过的毫秒数 恐怕没那么简单。 15184165622018-02-12T06:22:4215184166222018-02-12T06:23:42。我认为 06:22:4206:23:42 必须组合在一起,但 06:22:4206:23:43 不应该。 如果必须将 06:22:42 和 06:23:42 组合在一起,则跨度超过一分钟。通过传递性,06:23:42 和 06:24:42 也必须组合在一起,这会将所有时间归为一个组:-) @dnswlt 编辑了我的问题,添加了 2 个日志条目以使问题更清晰 好的,现在我明白了!只要与前一个条目的差异不大于一分钟,您就希望对值求和。那么@SalmanA 的使用窗口函数的解决方案可能是要走的路。【参考方案2】:

您可以使用LAG 检查当前行是否比前一行多 60 秒,并在每次发生这种情况时设置group_changed(虚拟列)。

在下一步中,对该列使用运行总和。这将创建一个group_number,您可以使用它来对第三步中的结果进行分组。

WITH cte1 AS (
    SELECT
        testdata.*,
        CASE WHEN start_time - LAG(start_time, 1, start_time) OVER (PARTITION BY user_id ORDER BY start_time) > 60 THEN 1 ELSE 0 END AS group_changed
    FROM testdata
), cte2 AS (
    SELECT
        cte1.*,
        SUM(group_changed) OVER (PARTITION BY user_id ORDER BY start_time) AS group_number
    FROM cte1
)
SELECT user_id, SUM(inserted_value)
FROM cte2
GROUP BY user_id, group_number

SQL Fiddle

【讨论】:

以上是关于按列值的差异对 SQL 中的数据进行分组的主要内容,如果未能解决你的问题,请参考以下文章

如何按列值的计数进行分组并对其进行排序?

Oracle - sql - 日期差异 - 按列值

HiveQL 按列值的子字符串分组并识别缺失的组

按列值分组的列值更新mysql排名

如何在 Pandas 数据框中按列值分组

按列值查询某些行的联合和分组