SQL实战:04之SQL中的分组问题求解
文章目录
- 概述
- 题目:分组问题求解
- 题解
- 第一步:求解差值
- 步骤二:窗口分组累加
- 完整SQL
- 总结
概述
最近刷题时遇到一些比较有意思的题目,乍一遇上时还不知道怎么求解,在灵光一闪时找到了问题的求解答案,感觉还比较有意思。题目虽然不是很难,但是要解答出来还是需要有一点思维的转弯,为了加深记忆,就选取了一道记录下来。
题目:分组问题求解
如下为电商公司用户访问时间数据,某个用户连续的访问记录如果时间间隔小于 60 秒,则分为同一个组.
表logs
字段名 | 数据类型 |
---|---|
id | bigint |
ts | bigint |
输入数据如下:
id | ts(秒) |
---|---|
1001 | 17523641234 |
1001 | 17523641256 |
1002 | 17523641278 |
1001 | 17523641334 |
1002 | 17523641434 |
1001 | 17523641534 |
1001 | 17523641544 |
1002 | 17523641634 |
1001 | 17523641638 |
1001 | 17523641654 |
参考结果:
id | ts(秒) | group |
---|---|---|
1001 | 17523641234 | 1 |
1001 | 17523641256 | 1 |
1001 | 17523641334 | 2 |
1001 | 17523641534 | 3 |
1001 | 17523641544 | 3 |
1001 | 17523641638 | 4 |
1001 | 17523641654 | 4 |
1002 | 17523641278 | 1 |
1002 | 17523641434 | 2 |
1002 | 17523641634 | 3 |
题解
第一步:求解差值
因为需要按照id分组,求解每个用户连续访问时间间隔小于60秒,则放在同一组,所以需要使用窗口函数,将上一行的ts移动到下一行,进行差值运算。
需要用到的知识点:
- PARTITION BY
- LAG():下移值
WITH temp_001 AS (SELECT id,ts,ts - LAG(ts,1,ts) OVER (PARTITION BY id ORDER BY ts ASC) AS ts_diffFROM logs
)
输出如下:
id | ts | ts_diff |
---|---|---|
1001 | 17523641234 | 17523641234 |
1001 | 17523641256 | 22 |
1001 | 17523641334 | 78 |
1001 | 17523641534 | 200 |
1001 | 17523641544 | 10 |
1001 | 17523641638 | 94 |
1001 | 17523641654 | 16 |
1002 | 17523641278 | 17523641278 |
1002 | 17523641434 | 156 |
1002 | 17523641634 | 200 |
步骤二:窗口分组累加
由上面的中间结果我们已经得出了一个ts的差值ts_diff,按照滑动 窗口顺序遍历窗口中的每一行数据,如果ts_diff的值大于指定的值60,则加1,否则加0。
第一行ts_diff的值大于60,则加1结果是1,然后第二行值为22加0,值还是为1
第二行和第一行的分组id就都是1了,分在了同一组达到了我们想要的效果。
第三行ts_diff的值是78大于60,加1结果为2,组的ID变成了2。
其他的依次如上。
SQL实现:
SELECT id,ts,SUM(IF(ts_diff>60,1,0)) OVER(PARTITION BY id ORDER BY ts ASC) AS group
FROM temp_001
输出结果:
id | ts(秒) | group |
---|---|---|
1001 | 17523641234 | 1 |
1001 | 17523641256 | 1 |
1001 | 17523641334 | 2 |
1001 | 17523641534 | 3 |
1001 | 17523641544 | 3 |
1001 | 17523641638 | 4 |
1001 | 17523641654 | 4 |
1002 | 17523641278 | 1 |
1002 | 17523641434 | 2 |
1002 | 17523641634 | 3 |
完整SQL
WITH temp_001 AS (SELECT id,ts,ts - LAG(ts,1,ts) OVER (PARTITION BY id ORDER BY ts ASC) AS ts_diffFROM logs
)
SELECT id,ts,SUM(IF(ts_diff>60,1,0)) OVER(PARTITION BY id ORDER BY ts ASC) AS group
FROM temp_001;
总结
这道题考察的知识点就是对窗口的理解和运用,第一步下移并做差值运算一般都可以快速想到,第二步就需要一点点思维的闪光。利用IF做归一话处理并使用滑动窗口求和得到分组的组号。