在 SQL 中按组中的行计算时间差异

Posted

技术标签:

【中文标题】在 SQL 中按组中的行计算时间差异【英文标题】:Calculate time diff by rows in group in SQL 【发布时间】:2020-05-13 20:33:29 【问题描述】:

我有一个包含用户站点访问和访问时间戳的数据集。如何按 id 对用户进行分组并计算 SQL 中用户访问之间的时间差?抱歉,对于新手问题,我是 SQL 的新手,试图构建一些报告。

我的数据集示例:

+----------+------------+---------------------+
| userID   | visitID    | vsitTS              |
+----------+------------+---------------------+
| user01   | visit01    | 2019-05-13 01:00:00 |
| user01   | visit02    | 2019-05-13 01:10:00 |
| user02   | visit01    | 2019-05-13 01:05:00 |
| user02   | visit02    | 2019-05-13 01:10:00 |
| user02   | visit03    | 2019-05-13 01:20:00 |
| user02   | visit04    | 2019-05-13 01:30:00 |
+----------+------------+---------------------+

我需要这样的结果:

+----------+------------+---------------+
| userID   | visitID    | time_dif_sec  |
+----------+------------+---------------+
| user01   | visit01    | 0             |
| user01   | visit02    | 10            |
| user02   | visit01    | 0             |
| user02   | visit02    | 5             |
| user02   | visit03    | 10            |
| user02   | visit04    | 10            |
+----------+------------+---------------+

【问题讨论】:

我删除了不一致的数据库标签。请仅使用您真正使用的数据库进行标记。 【参考方案1】:

以下是 BigQuery 标准 SQL

#standardSQL
SELECT 
  userID, 
  visitID, 
  IFNULL(TIMESTAMP_DIFF(visitTS, LAG(visitTS) OVER(win), SECOND), 0) AS time_diff_sec, 
  IFNULL(TIMESTAMP_DIFF(visitTS, LAG(visitTS) OVER(win), MINUTE), 0) AS time_diff_min 
FROM `project.dataset.table`
WINDOW win AS (PARTITION BY userid ORDER BY visitTS)   

如果应用到您的问题中的样本数据,如下例所示

#standardSQL
WITH `project.dataset.table` AS (
  SELECT 'user01' userID, 'visit01' visitID, TIMESTAMP '2019-05-13 01:00:00' visitTS UNION ALL
  SELECT 'user01', 'visit02', '2019-05-13 01:10:00' UNION ALL
  SELECT 'user02', 'visit01', '2019-05-13 01:05:00' UNION ALL
  SELECT 'user02', 'visit02', '2019-05-13 01:10:00' UNION ALL
  SELECT 'user02', 'visit03', '2019-05-13 01:20:00' UNION ALL
  SELECT 'user02', 'visit04', '2019-05-13 01:30:00' 
)
SELECT 
  userID, 
  visitID, 
  IFNULL(TIMESTAMP_DIFF(visitTS, LAG(visitTS) OVER(win), SECOND), 0) AS time_diff_sec, 
  IFNULL(TIMESTAMP_DIFF(visitTS, LAG(visitTS) OVER(win), MINUTE), 0) AS time_diff_min 
FROM `project.dataset.table`
WINDOW win AS (PARTITION BY userid ORDER BY visitTS)   

输出是

Row userID  visitID time_diff_sec   time_diff_min    
1   user01  visit01 0               0    
2   user01  visit02 600             10   
3   user02  visit01 0               0    
4   user02  visit02 300             5    
5   user02  visit03 600             10   
6   user02  visit04 600             10   

【讨论】:

完美运行,谢谢。还有一个问题,如何计算 time_diff 【参考方案2】:

您是否尝试过使用lag()?。它为您提供前一行值。 对于 Bigquery,您可以使用 UNIX_SECONDS() 将时间戳转换为秒,对于 mysql,您可以使用 to_seconds()

  select userid, visitid, 
  (UNIX_SECONDS(visitTS) - UNIX_SECONDS (lag(visitTS) over (partition by 
  userid order by userid, visitid))) as time_dif_sec 
  from table

【讨论】:

【参考方案3】:

您可以使用窗口函数lag() 获取“上一次”访问的时间戳,然后使用日期函数。

在 MySQL 中(窗口函数仅在 8.0 版本中可用):

select
    t.*,
    coalesce(
        to_seconds(visitTS)
        - to_seconds(lag(visiTS) over(partition by userID order by visitTS),
        0) ts_diff_seconds
from mytable t

在 BigQuery 中,您可以使用 timestamp_to_sec() 代替 to_seconds()

【讨论】:

以上是关于在 SQL 中按组中的行计算时间差异的主要内容,如果未能解决你的问题,请参考以下文章

在R中按组计算日期之间的差异

如何从 sql 中的 2 个表中按组聚合和计算平均值?

在SQL中按组计算移动平均数

在大熊猫DataFrame中按组删除异常值的更快方法[重复]

在具有重复行的 SQL Server 表中按组查找行号

在 RecyclerView 中按组划分元素