按 1 分钟间隔分组操作链 sql BigQuery

Posted

技术标签:

【中文标题】按 1 分钟间隔分组操作链 sql BigQuery【英文标题】:Group by 1 minute interval for the chain of actions sql BigQuery 【发布时间】:2021-01-05 16:50:42 【问题描述】:

我需要以 1 分钟的间隔对数据进行分组,以进行操作链。我的数据如下所示:

id    MetroId            Time             ActionName            refererurl
111     a          2020-09-01-09:19:00     First           www.***/a12345
111     b         2020-09-01-12:36:54      First           www.***/a12345
111     f         2020-09-01-12:36:56      First     www.***/xxxx
111     b         2020-09-01-12:36:58      Midpoint        www.***/a12345
111     f         2020-09-01-12:37:01      Midpoint    www.***/xxx
111     b          2020-09-01-12:37:03     Third           www.***/a12345
111     b          2020-09-01-12:37:09     Complete        www.***/a12345
222     d          2020-09-01-15:17:44     First           www.***/a2222
222     d          2020-09-01-15:17:48     Midpoint        www.***/a2222
222     d          2020-09-01-15:18:05     Third           www.***/a2222

我需要获取具有以下条件的数据:如果x_idx_urlaction_name 列具有Complete 值,则获取该值。如果没有Complete,则抓取Third,依此类推。

  ARRAY_AGG(current_query_result 
    ORDER BY CASE ActionName
      WHEN 'Complete' THEN 1
      WHEN 'Third' THEN 2
      WHEN 'Midpoint' THEN 3
      WHEN 'First' THEN 4
    END
    LIMIT 1
  )[OFFSET(0)]
FROM
    (
        SELECT d.id, c.Time, c.ActionName, c.refererurl, c.MetroId
        FROM
            `bq_query_table_c` c
            INNER JOIN `bq_table_d` d ON d.id = c.CreativeId
        WHERE
            c.refererurl LIKE "https://www.***/%"
            AND c.ActionName in ('First', 'Midpoint', 'Third', 'Complete')
    ) current_query_result
GROUP BY
    id,
    refererurl,
    MetroId 
    TIMESTAMP_SUB(
    PARSE_TIMESTAMP('%Y-%m-%d-%H:%M:%S', time), 
    INTERVAL MOD(UNIX_SECONDS(PARSE_TIMESTAMP('%Y-%m-%d-%H:%M:%S', time)), 1 * 60) 
    SECOND
  ) 

期望的输出:

id   MetroId         Time             ActionName            refererurl
111      a     2020-09-01-09:19:00     First           www.***/a12345
111     f     2020-09-01-12:37:01      Midpoint    www.***/xxx
111     b     2020-09-01-12:37:09     Complete        www.***/a12345
222     c      2020-09-01-15:18:05     Third           www.***/a2222

【问题讨论】:

例如,如果每 50 秒有 10 个动作(所以总共 10x50=500 秒),它被视为一个组,应该采取该组中的最后一个动作 - 对吗?请进一步确认或澄清 正确,有 4 个动作,每个动作持续约 5 秒。所有 4 个通常不超过一分钟。并且这 4 个动作被视为一组。同一 id 可以同时发生多个操作,但我为此添加了额外的分组。对不起,如果我让你更困惑...... 说实话 - 我仍然不清楚 - 您应该提供更好的输入和输出数据示例,其中包含更多组和更多操作,因此答案将真正解决您的用例而不是当前您的问题中非常简化的示例。对我来说,问题是对于解释你问题的不同方式——我会有不同的答案——但我不想为了回答而在空中开枪——我想首先真正了解你的情况 【参考方案1】:

以下是 BigQuery 标准 SQL

#standardSQL
WITH temp AS (
  SELECT *, PARSE_TIMESTAMP('%Y-%m-%d-%H:%M:%S', time) ts
  FROM `project.dataset.bq_table`
)
SELECT * EXCEPT (ts, time_lag) FROM (
  SELECT * ,
    TIMESTAMP_DIFF(LEAD(ts) OVER(PARTITION BY id ORDER BY ts), ts, SECOND) time_lag
  FROM (
    SELECT 
      AS VALUE ARRAY_AGG(t 
        ORDER BY STRPOS('First,Midpoint,Third,Complete',action_name) DESC 
        LIMIT 1
      )[OFFSET(0)]
    FROM temp t
    WHERE action_name IN ('First', 'Midpoint', 'Third', 'Complete')
    GROUP BY id, url, 
      TIMESTAMP_SUB(ts, INTERVAL MOD(UNIX_SECONDS(ts), 60) SECOND
      )   
  )
)
WHERE NOT IFNULL(time_lag, 777) < 60    

您可以使用您问题中的示例数据进行测试,如以下示例所示

#standardSQL
WITH `project.dataset.bq_table` AS (
  SELECT 111 id, '2020-09-01-09:19:00' time, 'First' action_name, 'www.***/a12345' url UNION ALL
  SELECT 111, '2020-09-01-12:36:54', 'First', 'www.***/a12345' UNION ALL
  SELECT 111, '2020-09-01-12:36:58', 'Midpoint', 'www.***/a12345' UNION ALL
  SELECT 111, '2020-09-01-12:37:03', 'Third', 'www.***/a12345' UNION ALL
  SELECT 111, '2020-09-01-12:37:09', 'Complete', 'www.***/a12345' UNION ALL
  SELECT 222, '2020-09-01-15:17:44', 'First', 'www.***/a2222' UNION ALL
  SELECT 222, '2020-09-01-15:17:48', 'Midpoint', 'www.***/a2222' UNION ALL
  SELECT 222, '2020-09-01-15:18:05', 'Third', 'www.***/a2222' 
), temp AS (
  SELECT *, PARSE_TIMESTAMP('%Y-%m-%d-%H:%M:%S', time) ts
  FROM `project.dataset.bq_table`
)
SELECT * EXCEPT (ts, time_lag) FROM (
  SELECT * ,
    TIMESTAMP_DIFF(LEAD(ts) OVER(PARTITION BY id ORDER BY ts), ts, SECOND) time_lag
  FROM (
    SELECT 
      AS VALUE ARRAY_AGG(t 
        ORDER BY STRPOS('First,Midpoint,Third,Complete',action_name) DESC 
        LIMIT 1
      )[OFFSET(0)]
    FROM temp t
    WHERE action_name IN ('First', 'Midpoint', 'Third', 'Complete')
    GROUP BY id, url, 
      TIMESTAMP_SUB(ts, INTERVAL MOD(UNIX_SECONDS(ts), 60) SECOND
      )   
  )
)
WHERE NOT IFNULL(time_lag, 777) < 60   

结果

Row     id      time                    action_name     url  
1       111     2020-09-01-09:19:00     First           www.***/a12345     
2       111     2020-09-01-12:37:09     Complete        www.***/a12345     
3       222     2020-09-01-15:18:05     Third           www.***/a2222    

注意:我仍然不能 100% 确定您的用例 - 但以上是基于目前讨论/评论的内容

【讨论】:

非常感谢,我会在星期一测试它。非常感谢! 我已经在您提供的数据样本上测试了代码。输出正是我需要的。在要求方面存在一些混乱。您要求提供更多输入和输出示例 - 我已经添加了这些示例以及 MetroId 归档的附加分组,这应该可以为您提供完整的图片。这是我拥有的完整代码。虽然您的代码确实适用于示例,但我无法使用它,因为它不包括 INNER JOIN bq_table_d` d ON d.id = c.CreativeId`。我尝试了所有包含此行的方法,但无法弄清楚。这超出了我的想象:((( 在您的问题中 - 只有一个表格 - bq_table - 并且逻辑没有提到超过此表格的任何用途 - 所以这就是我的回答中使用的内容。我不知道你提到的那些额外的表格是什么。无论如何-我认为您的原始问题已得到解答。 有两个表。 FROM `bq_query_table_c` c INNER JOIN `bq_table_d` d ON d.id = c.CreativeId 表 c 和表 d。它在原始帖子中。我认为这个问题也得到了回答,但不幸的是,它不适用于我需要在查询中执行的 INNER JOIN。可悲的是。感谢您的宝贵时间。 感谢您的反馈,谢谢,我会尝试找到一种插入 INNER JOIN 的方法,希望它会起作用。【参考方案2】:

这读起来像是一个间隙和岛屿问题,其中间隙大于 1 分钟,而岛屿代表“动作链”。

我将从构建代表岛屿的组开始:为此,您可以使用lag() 检索上一个动作时间,以及两个连续动作之间每间隔 1 分钟或更长时间的累积总和:

select t.*, 
    sum(case when time > timestamp_add(lag_time, interval 1 minute) then 1 else 0 end)
        over(partition by x_id, x_url order by time) grp
from (
    select d.id, c.time, c.actionname, c.refererurl, 
        lag(time) over(partition by id, refererurl order by time) lag_time
    from `bq_query_table_c` c
    inner join `bq_table_d` d on d.id = c.creativeid
    where c.refererurl like "https://www.***/%"
        and c.actionname in ('First', 'Midpoint', 'Third', 'Complete')
) t

grp 是岛屿标识符。

从那时起,我们可以使用您的原始逻辑来过滤每个组的首选操作。我们不需要以 1 分钟为间隔聚合 - 我们可以使用 grp 代替:

select   
    array_agg(t) order by case actionname
        when 'Complete' then 1 
        when 'Third'    then 2
        when 'midpoint' then 3
        when 'first'    then 4
    end limit 1)[offset(0)]
from (
    select t.*, 
        sum(case when time > timestamp_add(lag_time, interval 1 minute) then 1 else 0 end)
            over(partition by x_id, x_url order by time) grp
    from (
        select d.id, c.time, c.actionname, c.refererurl, 
            lag(time) over(partition by id, refererurl order by time) lag_time
        from `bq_query_table_c` c
        inner join `bq_table_d` d on d.id = c.creativeid
        where c.refererurl like "https://www.***/%"
            and c.actionname in ('First', 'Midpoint', 'Third', 'Complete')
    ) t
) t
group by id, refererurl, grp

请注意,例如,如果在一个岛上有两个“完成”操作,则不确定将选择哪一个(原始查询具有几乎相同的缺陷)。为了使结果具有确定性,您需要向ARRAY_AGG() 添加另一个排序条件,例如time

    array_agg(t) order by case actionname
        when 'Complete' then 1 
        when 'Third'    then 2
        when 'midpoint' then 3
        when 'first'    then 4
    end, time limit 1)[offset(0)]

【讨论】:

您好,感谢您的回复,我收到错误消息Unrecognized name: time 这里sum(case when time &gt; timestamp_add

以上是关于按 1 分钟间隔分组操作链 sql BigQuery的主要内容,如果未能解决你的问题,请参考以下文章

按 15 分钟间隔对 mysql 查询进行分组

按 10 分钟间隔对 pandas DataFrame 进行分组[重复]

使用 T-SQL 将 OHLC-Stockmarket 数据分组到多个时间范围内

Python - 按时间间隔分组的时间加权平均 Pandas

如何将时间列分别分组为 5 分钟间隔和最大值/最小值 SQL?

SQL查询中按日期、天间隔分组