需求:计算前1天,2天,3天的用户留存数量和用户留存率。
用户留存:某段时间内的新增用户,经过一段时间后,仍然使用应用的被认作是留存用户;这部分用户占当时新增用户的比例即是留存率。例如,5月份新增用户200,这200人在6月份启动过应用的有100人,7月份启动过应用的有80人,8月份启动过应用的有50人;则5月份新增用户一个月后的留存率是50%,二个月后的留存率是40%,三个月后的留存率是25%。
实现思路:例如,要计算2019-02-11的1日留存用户,需要知道两种数据,一是2019-02-10新增了哪些用户(在每日新增设备明细表中),二是2019-02-11的活跃用户有哪些(在每日活用户表中)。如果一个用户既是2019-02-10的新增用户,又是2019-02-11的活跃用户,此用户即为2019-02-10的1日留存用户。
数据来源:DWS层的日活表和新增设备明细表。
DWS层(每日留存用户明细表)
建表语句。
分析:
①此表采用了分区表的形式,以每天的日期为分区。因为每天都有自己的1日留存,2日留存,3日留存。。。等情况,数据量相对较大。但因为都是针对新增设备的留存明细,数据量只能说相对较大,并没有达到非用分区表不可的地步。
②表中有两个重要的字段,设备新增时间和直到今日的留存天数。例如,在2010-02-11那天,数据可能出现设备新增日期为2019-02-10,留存天数为1的数据;设备新增日期为2019-02-09,留存天数为2的数据。。。等等。
③DWS层还只是一个留存明细表,具体有多少留存,还需要去ADS做count操作。
drop table if exists dws_user_retention_day; create external table dws_user_retention_day ( `mid_id` string COMMENT '设备唯一标识', `user_id` string COMMENT '用户标识', `version_code` string COMMENT '程序版本号', `version_name` string COMMENT '程序版本名', `lang` string COMMENT '系统语言', `source` string COMMENT '渠道号', `os` string COMMENT '安卓系统版本', `area` string COMMENT '区域', `model` string COMMENT '手机型号', `brand` string COMMENT '手机品牌', `sdk_version` string COMMENT 'sdkVersion', `gmail` string COMMENT 'gmail', `height_width` string COMMENT '屏幕宽高', `app_time` string COMMENT '客户端日志产生时的时间', `network` string COMMENT '网络模式', `lng` string COMMENT '经度', `lat` string COMMENT '纬度', `create_date` string comment '设备新增时间', `retention_day` int comment '截止当前日期留存天数' ) COMMENT '每日用户留存情况' PARTITIONED BY (`dt` string) stored as parquet location '/warehouse/gmall/dws/dws_user_retention_day/'
数据导入,以2019-02-11的1日,2日,3日留存为例
分析:
①在sql中,用户活跃日期固定为统计日期(2019-02-11),而设备新增日期随着留存天数retention_day 不同,在统计日期(2019-02-11)上减去相应的天数。
②将所有的sql结果进行union all,就得到了2019-02-11相对于2019-02-10的1日留存,相对于2019-02-09的2日留存和相对于2019-02-08的3日留存。若要计算其他留存天数,进行类似重复操作即可。
insert overwrite table dws_user_retention_day partition(dt="2019-02-11") select nm.mid_id, nm.user_id, nm.version_code, nm.version_name, nm.lang, nm.source, nm.os, nm.area, nm.model, nm.brand, nm.sdk_version, nm.gmail, nm.height_width, nm.app_time, nm.network, nm.lng, nm.lat, nm.create_date, 1 retention_day from dws_uv_detail_day ud join dws_new_mid_day nm on ud.mid_id =nm.mid_id where ud.dt='2019-02-11' and nm.create_date=date_add('2019-02-11',-1) union all select nm.mid_id, nm.user_id , nm.version_code , nm.version_name , nm.lang , nm.source, nm.os, nm.area, nm.model, nm.brand, nm.sdk_version, nm.gmail, nm.height_width, nm.app_time, nm.network, nm.lng, nm.lat, nm.create_date, 2 retention_day from dws_uv_detail_day ud join dws_new_mid_day nm on ud.mid_id =nm.mid_id where ud.dt='2019-02-11' and nm.create_date=date_add('2019-02-11',-2) union all select nm.mid_id, nm.user_id , nm.version_code , nm.version_name , nm.lang , nm.source, nm.os, nm.area, nm.model, nm.brand, nm.sdk_version, nm.gmail, nm.height_width, nm.app_time, nm.network, nm.lng, nm.lat, nm.create_date, 3 retention_day from dws_uv_detail_day ud join dws_new_mid_day nm on ud.mid_id =nm.mid_id where ud.dt='2019-02-11' and nm.create_date=date_add('2019-02-11',-3);
ADS层留存用户数和留存用户比率
建表语句
分析:注意,此表既包括留存数量,也包括用户留存比率(某日的n天留存,有2个条件)
drop table if exists ads_user_retention_day_rate; create external table ads_user_retention_day_rate ( `stat_date` string comment '统计日期', `create_date` string comment '设备新增日期', `retention_day` int comment '截止当前日期留存天数', `retention_count` bigint comment '留存数量', `new_mid_count` bigint comment '当日设备新增数量', `retention_ratio` decimal(10,2) comment '留存率' ) COMMENT '每日用户留存情况' row format delimited fields terminated by ' ' location '/warehouse/gmall/ads/ads_user_retention_day_rate/';
导入数据。
①数据来源于上一张用户每日留存明细表。
②where条件dt='2019-02-11',说明要拿出每日留存明细表中2019-02-11的所有数据(不同留存天数)。
②group by create_date,retention_day,分组条件有2个,其实只用一个条件分组也可以,但是select的字段必须是分组字段,或者在聚合函数里,为了方便,用这两个条件分组,且不影响结果。
③留存率 = 留存用户数 / 当天新增用户总数。所以需要 join ADS层的ads_new_mid_count ,里面保存了每日新增的用户(设备)数量。
insert into table ads_user_retention_day_rate select '2019-02-11', ur.create_date, ur.retention_day, ur.retention_count, nc.new_mid_count, ur.retention_count/nc.new_mid_count*100 from ( select create_date, retention_day, count(*) retention_count from dws_user_retention_day where dt='2019-02-11' group by create_date,retention_day ) ur join ads_new_mid_count nc on nc.create_date=ur.create_date;