如何用SQL统计用户复购(or留存)数据

在工作中,如果涉及到用户交易的数据,我们可能会经常统计每天的新户(用户在当日首次完成交易)交易用户数、新户在次日、T+n日、次周、下下周、在自然月当月、在下月、在下下月等不同的时间周期内的复购数据。

用户在首次交易的日期之后的第n天,再次发生了交易行为,我们称之为用户在第n天的复购。其实,用户在第n天产生了复购行为,也就是用户在第n天之后还是留存的。复购和留存的意思是一样的。这些统计数据其实是非常常见的,所以我在这些常见的复购统计情况做个总结。在这之前,需要先了解一些常见的SQL函数。

统计复购数据涉及的SQL函数

  • datediff(date1,date2):返回日期date1和date2之间相差的天数
select datediff('2020-08-13','2020-07-31')
输出:13
  • months_between(date1,date2):返回日期date1和date2之间相差的月份数,以小数形式展示。
select months_between('2020-08-13','2020-07-20')
输出:0.77419355

但在实际中,我们一般想要的两个日期之间相差的月份数是一个整数,比如2020-07-20和2020-08-13,相差了一个月,我们希望返回的结果是1,而不是一个很精确的小数。这时可以用round函数来取整。

select   round(months_between('2020-08-13','2020-07-20'))
输出:1.0
  • min(trans_date) over(partition by uid):以用户的UID进行分区,计算出该用户最小的交易日期,也就是用户首次交易的日期
  • max(trans_date) over(partition by uid):以用户的UID进行分区,计算出该用户最大的交易日期,也就是用户最近一次完成交易的日期

使用SQL统计用户复购数据

假设我们现在有一张全量的用户交易数据表,记录了所有用户在历史所有时期的交易行为,用户交易一次,就产生一条记录。具体的表结构如下:

create table t_user_trans_info(
index_id    int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
trans_date  datetime NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT '交易日期和时间',
uid         varchar(20) NOT NULL DEFAULT ' ' COMMENT '用户UID',
order_id    varchar(20) NOT NULL DEFAULT ' ' COMMENT '用户交易订单号',
amount      double  NOT NULL DEFAULT '0.0 ' COMMENT '用户交易金额(元)',
PRIMARY KEY (index_id)  ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ;

计算每天的新户交易量

首先,我们可以用min(trans_date) over(partition by uid)这个函数对每个用户的首次交易日期做个标识,记为first_date,然后用datediff这个函数,计算每个用户的交易日期和他本身的首单日期之间的差额,若表里的记录满足datediff(trans_date,ffirst_date)=0,则这些记录就是新户产生的交易数据,它们就参与计算。由于在表里的trans_date字段保存的是日期加时间,我们需要的是日期,所以一般都要用**date(trans_date)**来取得用户的交易日期。具体的SQL如下:

select    date_act,
count(distinct case when datediff(date_act,first_date)=0 then uid end) as user_cnt,  --每天的新户交易用户数
count(distinct case when datediff(date_act,first_date)=0 then order_id end) as order_cnt,  --每天的新户交易订单数
sum(case when datediff(date_act,first_date)=0 then amount   end) as order_amt  --每天的新户交易金额
from(
	date(trans_date) as date_act,
    order_id,
	amount,
    min(date(trans_date))  over(partition by uid) as first_date  --首单日期标识
    from  t_user_trans_info  ) t
    group by date_act

计算新户在T+n日的复购交易量

计算新户在第n天的复购情况,就是把计算新户每天的交易量中的函数datediff(trans_date,ffirst_date)=0改为datediff(trans_date,ffirst_date)=n就可以了。SQL如下:

select    date_act,
count(distinct case when datediff(date_act,first_date)=n   then uid end) as user_cnt,  --新户在T+n日的交易用户数
count(distinct case when datediff(date_act,first_date)=n  then order_id end) as order_cnt,  --新户在T+n日的交易订单数
sum(case when datediff(date_act,first_date)=n   then amount   end) as order_amt --新户在T+n日的交易金额
from(
	date(trans_date) as date_act,
    order_id,
	amount,
    min(date(trans_date))  over(partition by uid) as first_date  --首单日期标识
    from  t_user_trans_info )  t
    group by date_act

计算新户在自然月当月、下月的成交量

计算新户在自然月当月、下月的成交数据,其实和统计新户在T+n日的复购数据是类似的,不过要将datediff(trans_date,first_date)换成了months_between(trans_date,first_date)。同时,为了符合我们常用的逻辑,我们还会对相差的月份数取整。因此,当表里的记录满足round(months_between(trans_date,first_date))=0时,这些记录则参与新户在自然月当月的成交数据的计算;当表里的记录满足round(months_between(trans_date,first_date))=1时,这些记录则参与新户在下月的成交数据的计算。SQL代码如下:

select    date_act,
count(distinct case when round(months_between(date_act,first_date))=0 then uid end) as user_cnt,  --新户在自然月当月的交易用户数
count(distinct case when round(months_between(date_act,first_date))=0 then order_id end) as order_cnt,  --新户在自然月当月的交易订单数
sum(case when round(months_between(date_act,first_date))=0  then amount   end) as order_amt,  --新户在自然月当月的交易金额
count(distinct case when round(months_between(date_act,first_date))=1 then uid end) as user_cnt,  --新户在次月的交易用户数
count(distinct case when round(months_between(date_act,first_date))=1   then order_id end) as order_cnt,  --新户在次月的交易订单数
sum(case when round(months_between(date_act,first_date))=1   then amount   end) as order_amt  --新户在次月的交易金额
from(
	date(trans_date) as date_act,
    order_id,
	amount,
    min(date(trans_date))  over(partition by uid) as first_date  --首单日期标识
    from  t_user_trans_info)  t
    group by date_act
    

猜你喜欢

转载自blog.csdn.net/Monica114/article/details/107982245