前阵子,在工作中收到一个业务部门发起的取数工单,取数口径比较有意思。前阵子,因为其他事情比较忙,现在抽空记录一下问题。
部分业务背景已省略,简化问题如下:
…筛选连续三个月及以上有交易流水的用户…
这个问题看起来很简单,最笨蛋的方法无非就是筛选1,2,3,4,5,6,7,8,9,10,11,12月每个月有交易的用户,形成12个临时表,然后再暴力遍历。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
with tmp_1 as (
select cust_id
from trx_act
where trx_date between '2022-01-01' and '2022-01-31'
group by cust_id
),tmp_2 as (
select cust_id
from trx_act
where trx_date between '2022-02-01' and '2022-02-29'
group by cust_id
),
.....
,tmp_12 as (
select cust_id
from trx_act
where trx_date between '2022-12-01' and '2022-12-31'
group by cust_id
)
select a.cust_id
from tmp_1 a
left join tmp_2 b
on a.cust_id = b.cust_id
left join tmp_3 c
on a.cust_id = c.cust_id
where b.cust_id is not null and c.cust_id is not null
.....
|
当然,我这里又想出了两种比较好的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
-- 方法1
select cust_id,action,count(*) as times
from (
select cust_id,action,(month - rank_num) as flag
from (
select cust_id,action,month,rank() over(partion by cust_id,action order by month) as rank_num
from tbl
group by month,action,cust_id a
)b
)
group by cust_id,action,flag
having times >= 3;
-- 方法二
select distinct cust_id,action
from (
select cust_id
,action
,month
,lag(month,2) over(partition by cust_id,action order by month) as lag_2_month
from tbl
group by month,cust,action
)a
where month - lag_2_month = 2; -- 某一行的月份 - 前两行的月份的差值等于2
|
方法一在取一些连续n次行为的数据提取的时候很常用,原理就是将特定行为去重排序,取日期/月份和序号的差值作为计数器
数学原理:
一般地,等差数列 {an} = a1,a2,a3…… ,{bn} = b1,b2,b3……
那么,{an} - {bn} 就是一个常数数列。
方法二的数学原理和方法一的数学原理差不多,差别在于方法二使用了一个 lag()
窗口函数。
1
2
3
4
|
LAG(<expression>[,offset[, default_value]]) OVER (
PARTITION BY expr,...
ORDER BY expr [ASC|DESC],...
)
|
expression#
LAG()
函数返回expression
当前行之前的行的值,其值为offset
其分区或结果集中的行数。
offset#
offset
是从当前行返回的行数,以获取值。offset
必须是零或文字正整数。如果offset
为零,则LAG()
函数计算expression
当前行的值。如果未指定offset
,则LAG()
默认情况下函数使用一个。
default_value#
如果没有前一行,则LAG()
函数返回default_value
。例如,如果offset为2,则第一行的返回值为default_value
。如果省略default_value
,则默认LAG()
返回函数NULL
。
PARTITION BY
子句#
PARTITION BY
子句将结果集中的行划分LAG()
为应用函数的分区。如果省略PARTITION BY
子句,LAG()
函数会将整个结果集视为单个分区。
ORDER BY
子句#
ORDER BY
子句指定在LAG()
应用函数之前每个分区中的行的顺序。
LAG()
函数可用于计算当前行和上一行之间的差异。