在 SQL (Hive) 中使用 collect_list 函数来聚合用户序列

Posted

技术标签:

【中文标题】在 SQL (Hive) 中使用 collect_list 函数来聚合用户序列【英文标题】:Using collect_list function in SQL (Hive) to aggregate user sequence 【发布时间】:2018-05-04 01:01:52 【问题描述】:

我有一个代表个人使用情况的数据集,他们必须在其中输入值。

除了第 1 步的“名字”之外,可以按任何顺序填写表格。

时间戳用于暗示表单完成的顺序。

例如 - 用户 12345678 于 2017 年 10 月 25 日 04:58:08 开始填写表格,并按顺序填写表格。

用户 12345679 在同一天 05:00:02 开始填写表格,但只完成了第 2 步

用户 12345680 于 05:05:06 开始填写表格,但分心并没有超过第 1 步,但在完成前又返回了两次

用户 12345681 于 06:31:12 开始填写表单并从步骤 1 开始,但随后随机填写表单。

|    date  |     timestamp      | user_id  |  visit_id   |      event    |  event_seq  |
|--------------------------------------------------------------------------------------|
|2017-10-25| 2017-10-25 04:58:08| 12345678 |    1234     |   firstname   |       1     |
|2017-10-25| 2017-10-25 04:58:20| 12345678 |    1234     |   lastname    |       2     |
|2017-10-25| 2017-10-25 04:58:35| 12345678 |    1234     |      dob      |       3     |
|2017-10-25| 2017-10-25 04:58:40| 12345678 |    1234     |   postcode    |       4     |
|2017-10-25| 2017-10-25 04:58:40| 12345678 |    1234     |     email     |       5     |
|2017-10-25| 2017-10-25 05:00:02| 12345679 |    1235     |   firstname   |       1     |
|2017-10-25| 2017-10-25 05:00:10| 12345679 |    1235     |   lastname    |       2     |
|2017-10-25| 2017-10-25 05:05:06| 12345680 |    1236     |   firstname   |       1     |
|2017-10-25| 2017-10-25 05:30:24| 12345680 |    1236     |   firstname   |       1     |
|2017-10-25| 2017-10-25 06:17:24| 12345680 |    1236     |   firstname   |       1     |
|2017-10-25| 2017-10-25 06:20:30| 12345680 |    1236     |   lastname    |       2     |
|2017-10-25| 2017-10-25 06:20:45| 12345680 |    1236     |      dob      |       3     |
|2017-10-25| 2017-10-25 06:20:45| 12345680 |    1236     |   postcode    |       4     |
|2017-10-25| 2017-10-25 06:20:45| 12345680 |    1236     |     email     |       5     |
|2017-10-25| 2017-10-25 06:31:12| 12345681 |    1237     |   firstname   |       1     |
|2017-10-25| 2017-10-25 06:31:18| 12345681 |    1237     |     email     |       5     |
|2017-10-25| 2017-10-25 06:31:50| 12345681 |    1237     |   lastname    |       2     |
|2017-10-25| 2017-10-25 06:32:16| 12345681 |    1237     |   postcode    |       4     |
|2017-10-25| 2017-10-25 06:32:40| 12345681 |    1237     |      dob      |       3     |

我编写的代码如下,并引用了一个预先存在的表,其中 CASE WHEN 用于在名为“事件”的变量中为表单的每个步骤分配一个数字:

SELECT date
,time_stamp
,user_id
,visit_id
,collect_list(events) as event_seq
FROM my_table
GROUP BY date
,start_time
time_stamp
,user_id
,visit_id 

正如预期的那样,这似乎将用户 12345680 的所有交互都列在一个字符串中;

|    date  |     timestamp      | user_id  |  visit_id   |  event_seq  |
|----------------------------------------------------------------------|
|2017-10-25| 2017-10-25 04:58:08| 12345678 |    1234     |  1,2,3,4,5  |
|2017-10-25| 2017-10-25 05:00:02| 12345679 |    1235     |     1,2     |
|2017-10-25| 2017-10-25 05:05:06| 12345680 |    1236     |1,1,1,2,3,4,5| 
|2017-10-25| 2017-10-25 06:31:12| 12345681 |    1237     |  1,5,2,4,3, | 

但是,我希望看到的是由序列中的第一个事件标记的每一行,类似于下面的结果集,其中用户 12345680 的每个重新启动都发生在不同的行上。

|    date  |     timestamp      | user_id  |  visit_id   |  event_seq  |
|----------------------------------------------------------------------|
|2017-10-25| 2017-10-25 04:58:08| 12345678 |    1234     |  1,2,3,4,5  |
|2017-10-25| 2017-10-25 05:00:02| 12345679 |    1235     |     1,2     |
|2017-10-25| 2017-10-25 05:05:06| 12345680 |    1236     |      1      |
|2017-10-25| 2017-10-25 05:30:24| 12345680 |    1236     |      1      |
|2017-10-25| 2017-10-25 06:17:24| 12345680 |    1236     |  1,2,3,4,5  |      
|2017-10-25| 2017-10-25 06:31:12| 12345681 |    1237     |  1,5,2,4,3, | 

任何人都可以就我如何使用 collect_list 来实现我想要的结果集提供任何指导吗?

【问题讨论】:

嗨,您的 sql 引用了一个名为 start_time 的列。那是表格中的一列吗?你没有反映出来.... 【参考方案1】:

从您的 SQL 中,您似乎在表中有一个名为 start_time 的列。假设您有一个,请参阅下面的解决方案

表格

CREATE EXTERNAL TABLE my_table(
  event_date DATE,
  event_start_timestamp TIMESTAMP,
  event_timestamp TIMESTAMP,
  user_id STRING,
  visit_id STRING,
  event STRING,
  event_seq STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' 
STORED AS TEXTFILE;

数据

2017-10-25| 2017-10-25 04:58:08| 2017-10-25 04:58:08| 12345678 |    1234     |   firstname   |       1     
2017-10-25| 2017-10-25 04:58:08| 2017-10-25 04:58:20| 12345678 |    1234     |   lastname    |       2     
2017-10-25| 2017-10-25 04:58:08| 2017-10-25 04:58:35| 12345678 |    1234     |      dob      |       3     
2017-10-25| 2017-10-25 04:58:08| 2017-10-25 04:58:40| 12345678 |    1234     |   postcode    |       4     
2017-10-25| 2017-10-25 04:58:08| 2017-10-25 04:58:40| 12345678 |    1234     |     email     |       5     
2017-10-25| 2017-10-25 05:00:02| 2017-10-25 05:00:02| 12345679 |    1235     |   firstname   |       1     
2017-10-25| 2017-10-25 05:00:02| 2017-10-25 05:00:10| 12345679 |    1235     |   lastname    |       2     
2017-10-25| 2017-10-25 05:05:06| 2017-10-25 05:05:06| 12345680 |    1236     |   firstname   |       1     
2017-10-25| 2017-10-25 05:30:24| 2017-10-25 05:30:24| 12345680 |    1236     |   firstname   |       1     
2017-10-25| 2017-10-25 06:17:24| 2017-10-25 06:17:24| 12345680 |    1236     |   firstname   |       1     
2017-10-25| 2017-10-25 06:17:24| 2017-10-25 06:20:30| 12345680 |    1236     |   lastname    |       2     
2017-10-25| 2017-10-25 06:17:24| 2017-10-25 06:20:45| 12345680 |    1236     |      dob      |       3     
2017-10-25| 2017-10-25 06:17:24| 2017-10-25 06:20:45| 12345680 |    1236     |   postcode    |       4     
2017-10-25| 2017-10-25 06:17:24| 2017-10-25 06:20:45| 12345680 |    1236     |     email     |       5     
2017-10-25| 2017-10-25 06:31:12| 2017-10-25 06:31:12| 12345681 |    1237     |   firstname   |       1     
2017-10-25| 2017-10-25 06:31:12| 2017-10-25 06:31:18| 12345681 |    1237     |     email     |       5     
2017-10-25| 2017-10-25 06:31:12| 2017-10-25 06:31:50| 12345681 |    1237     |   lastname    |       2     
2017-10-25| 2017-10-25 06:31:12| 2017-10-25 06:32:16| 12345681 |    1237     |   postcode    |       4     
2017-10-25| 2017-10-25 06:31:12| 2017-10-25 06:32:40| 12345681 |    1237     |      dob      |       3   

SQL 查询

SELECT event_date,
       user_id, 
       visit_id, 
       event_start_timestamp, 
       collect_list(event_seq)
  FROM (SELECT event_date, 
               event_start_timestamp, 
               event_timestamp, 
               user_id, 
               visit_id, 
               event_seq 
          FROM my_table 
          SORT BY user_id, visit_id, event_start_timestamp, event_timestamp ASC) v
 GROUP BY event_date, user_id, visit_id, event_start_timestamp ;

输出

2017-10-25   12345678       1234        2017-10-25 04:58:08 ["       1     ","       2     ","       3     ","       4     ","       5     "]
2017-10-25   12345679       1235        2017-10-25 05:00:02 ["       1     ","       2     "]
2017-10-25   12345680       1236        2017-10-25 05:05:06 ["       1     "]
2017-10-25   12345680       1236        2017-10-25 05:30:24 ["       1     "]
2017-10-25   12345680       1236        2017-10-25 06:17:24 ["       1     ","       2     ","       3     ","       4     ","       5     "]
2017-10-25   12345681       1237        2017-10-25 06:31:12 ["       1     ","       5     ","       2     ","       4     ","       3     "]

让我们知道这是否可行!

请不要将列名用作 DATE、TIMESTAMP 等,它们是保留字 :)

【讨论】:

【参考方案2】:

使用 LEAD 窗口函数根据 event_seq 列生成新列 next_event_seq。这将为您提供另一列,其中包含每行的下一个事件序列。现在在 where 子句中使用它来比较 event_seq。任何时候 event_seq 小于 next_event_seq,这意味着它是序列的一部分,需要分组。

select date,time_stamp,user_id,visit_id,collect_list(events) as event_seq
from
   ( select *,lead(event_seq,1) over (order by date,timestamp,user_id,event_seq) as next_event_seq from my_table ) T
where event_seq < T.next_event_seq
group by date,time_stamp,user_id,visit_id 

【讨论】:

@jimiclapton 将 event_seq 添加到 sort in order by 子句中,它应该可以工作。请注意,我们只需要对它进行排序即可获得序列。我已经编辑了答案。让我知道如果可行。 谢谢@inquisitive_mind 这在给定的数据集上似乎对您有用吗?对我来说,这并没有达到预期的效果,而是导致每个单独的交互在单独的行上仅按时间戳排序。因此,分组似乎不起作用。

以上是关于在 SQL (Hive) 中使用 collect_list 函数来聚合用户序列的主要内容,如果未能解决你的问题,请参考以下文章

如何在 hive udf 中使用 collect_set 的结果 - 评估方法?

hive中对多行进行合并—collect_set&collect_list函数

HiveQL collect_list保持顺序小记

Hive 的collect_set使用详解

SQL实现将多行记录合并成一行

SQL实现将多行记录合并成一行