SQL练习七—留存率问题

目录

一 如何计算留存率

1.先把想要的数据提取出来观察:用户id、日期、次日日期

2.留存情况:我们希望当前日期和次日日期对应起来

3.留存率计算:留存的(有次日信息)/全部 

4.整理思路,代码优化

 二 留存率实战练习—2021年11月每天新用户的次日留存率

一 如何计算留存率
  • 公式:留存率 = (留存用户数 / 初始用户数)× 100%。
  • 举例:假设一个应用在第一天有 1000 个新用户注册,到第 7 天时,这 1000 个用户中有 300 个仍然在使用该应用,那么第 7 天的留存率 = (300 / 1000)× 100% = 30%。

问题分析:

1.先把想要的数据提取出来观察:用户id、日期、次日日期

去重:去掉一个用户一日多次登录记录

select distinct device_id,date,date_add(date,interval 1 day)as ciri
from question_practice_detail
order by device_id,date

2.留存情况:我们希望当前日期和次日日期对应起来

如果没有就是次日没来,如果有就代表次日留存

with t1 as(
select distinct device_id,date,date_add(date,interval 1 day)as ciri
from question_practice_detail
order by device_id,date
)
select a.device_id,a.date as '登录日期',b.date as '次日日期' from  t1 as a
left join t1 as b
on a.device_id=b.device_id and a.date+1=b.date

3.留存率计算:留存的(有次日信息)/全部 

  count(次日)/count(device_id)

4.整理思路,代码优化
select round(count(yd)/count(device_id),4) as avg_ret
from(
select distinct q1.device_id as device_id,q1.date as td ,q2.date as yd
from question_practice_detail as q1
left join  question_practice_detail  as q2
on q1.device_id=q2.device_id and q2.date=date_add(q1.date,interval 1 day)
    ) a

 二 留存率实战练习—2021年11月每天新用户的次日留存率

t1:用户,注册日期(最小的登录日期),按日期group 计每日新增用户数 

t2:(用户的登录信息) 用户,登录时间(进出跨天两天都计入)

把这个注册信息和登录信息连接on用户id和日期(检查次日是否登录 注册日期+1=登录日期)

select
    regdt,
    round(count(t3.uid) / count(t2.uid), 2) as uv_left_rate
from
    (
        select
            uid,
            regdt,
            count(uid) over (
                partition by
                    regdt
            ) as uv
        from
            (
                select
                    uid,
                    date (min(in_time)) as regdt
                from
                    tb_user_log
                group by
                    uid
            ) t1
    ) t2
    left join (
        select
            uid,
            date (in_time) as dt
        from
            tb_user_log
        union
        select
            uid,
            date (out_time) as dt
        from
            tb_user_log
    ) t3 on t3.dt = date_add (regdt, interval 1 day)
    and t2.uid = t3.uid
where
    date_format (regdt, '%Y-%m') = '2021-11'
group by
    regdt

<think>我们正在处理一个关于SQL留存率计算的问题。用户希望了解如何用SQL计算留存率,并请求示例查询。根据引用内容,留存率通常指用户在某天(或某个时间段)首次使用产品后,在后续特定时间(如次日、7日后等)仍然活跃的比例。常见的留存率计算步骤:1.确定每个用户的首次活跃日期(或其他起始事件日期)。2.确定用户在后续目标日期(如次日、第7日等)是否活跃。3.按起始日期分组,计算该起始日期的总用户数(即初始用户数)和满足后续活跃条件的用户数(即留存用户数)。4.计算留存率:留存用户数/初始用户数。引用[2]和[4]提供了SQL示例,特别是引用[4]提供了一个具体的查询示例,计算的是次日留存率。我们将基于这些引用,构建一个通用的SQL留存率计算示例,以次日留存率为例。注意:留存率计算可能因具体需求(如时间窗口、是否跨多日等)而有所不同,但核心思路一致。示例场景:有一个用户活跃表(例如名为`user_activity`),包含字段:-`user_id`:用户ID-`activity_date`:用户活跃日期(假设为日期类型)目标:计算每日的次日留存率(即某日新增或活跃的用户中,第二天仍然活跃的比例)。步骤:1.先获取每个用户的首次活跃日期(如果是计算新增用户的留存,则使用首次活跃日期;如果是计算所有活跃用户的留存,则可能使用任意一次活跃作为起始,但通常留存率针对新增用户计算)。但注意,引用[4]中并没有区分新增还是活跃,而是针对任意一天活跃的用户,看他们第二天是否活跃。这种称为“滚动留存”或“经典留存”。引用[4]的示例:```sqlselectcount(q2.device_id)/count(q1.device_id)asavg_retfrom(selectdistinctdevice_id,datefromquestion_practice_detail)asq1leftjoin(selectdistinctdevice_id,datefromquestion_practice_detail)asq2onq1.device_id=q2.device_idandq2.date=date_add(q1.date,interval1day)```这个查询计算的是整体平均次日留存概率。它没有按起始日期分组,而是计算了所有日期的平均留存率。如果我们希望按起始日期分组,得到每天的次日留存率,可以修改为:```sqlSELECTq1.dateASstart_date,COUNT(DISTINCTq2.device_id)ASretained_users,COUNT(DISTINCTq1.device_id)AStotal_users,COUNT(DISTINCTq2.device_id)*1.0/COUNT(DISTINCTq1.device_id)ASretention_rateFROM(SELECTDISTINCTdevice_id,dateFROMquestion_practice_detail)ASq1LEFTJOIN(SELECTDISTINCTdevice_id,dateFROMquestion_practice_detail)ASq2ONq1.device_id=q2.device_idANDq2.date=DATE_ADD(q1.date,INTERVAL1DAY)GROUPBYq1.date;```注意:-这里使用`DISTINCT`是因为同一个用户同一天可能有多次活跃,我们只关心该用户当天是否活跃。-使用`LEFTJOIN`是为了包括所有起始日的用户,即使他们没有在次日活跃。-留存率计算时,将分子(留存用户数)除以分母(起始日用户数),并转换为浮点数(乘以1.0)以避免整数除法。但是,上面的查询有一个问题:如果某一天没有用户活跃,那么这一天的留存率就不会被计算。另外,如果起始日之后的一天没有数据,那么留存用户数就会为0,这是符合预期的。另外,引用[2]中使用了首次登录时间,然后计算留存。如果我们只关心新用户的留存(即每个用户只以其首次登录日作为起始日),那么我们需要先计算每个用户的首次登录日,然后看他们在首次登录日的次日是否活跃。新用户次日留存率计算示例:```sqlWITHFirstLoginAS(SELECTuser_id,MIN(activity_date)ASfirst_login_dateFROMuser_activityGROUPBYuser_id)SELECTfirst_login_date,COUNT(DISTINCTCASEWHENa.activity_date=DATE_ADD(fl.first_login_date,INTERVAL1DAY)THENfl.user_idEND)ASretained_users,COUNT(DISTINCTfl.user_id)ASnew_users,COUNT(DISTINCTCASEWHENa.activity_date=DATE_ADD(fl.first_login_date,INTERVAL1DAY)THENfl.user_idEND)*1.0/COUNT(DISTINCTfl.user_id)ASretention_rateFROMFirstLoginflLEFTJOINuser_activityaONfl.user_id=a.user_idGROUPBYfirst_login_date;```解释:-先使用CTE(CommonTableExpression)计算每个用户的首次登录日期(`FirstLogin`)。-然后,将`FirstLogin`表与原始活跃表`user_activity`左连接,这样每个用户的所有活跃记录都会关联到其首次登录日。-在SELECT中,使用条件计数(CASEWHEN)来统计在首次登录次日活跃的用户数(即留存用户数)。-按首次登录日期分组,计算每天的留存率。注意:这里我们只关心首次登录日次日的活跃情况,所以条件为`a.activity_date=DATE_ADD(fl.first_login_date,INTERVAL1DAY)`。以上两种方式都是常见的留存率计算方法,具体使用哪一种取决于业务需求(是计算每日活跃用户的次日留存,还是计算每日新增用户的次日留存)。现在,我们根据用户的问题,提供一个通用的次日留存率查询示例(针对每日活跃用户,而非仅新增用户),并按天展示留存率。示例查询(每日活跃用户的次日留存率):```sqlSELECTstart_date,COUNT(DISTINCTuser_id)AStotal_users,--起始日活跃用户数COUNT(DISTINCTretained_user_id)ASretained_users,--次日留存用户数COUNT(DISTINCTretained_user_id)*1.0/COUNT(DISTINCTuser_id)ASretention_rateFROM(SELECTa1.user_id,a1.activity_dateASstart_date,a2.user_idASretained_user_idFROM(SELECTDISTINCTuser_id,activity_dateFROMuser_activity)a1LEFTJOIN(SELECTDISTINCTuser_id,activity_dateFROMuser_activity)a2ONa1.user_id=a2.user_idANDa2.activity_date=DATE_ADD(a1.activity_date,INTERVAL1DAY))ASretention_dataGROUPBYstart_dateORDERBYstart_date;```在这个查询中:-子查询`retention_data`对每个用户在每个活跃日期(作为起始日)进行左连接,连接条件是同一个用户在起始日次日活跃的记录。如果没有次日活跃,则`retained_user_id`为NULL。-外层查询按起始日分组,计算每个起始日的总用户数(去重,因为一个用户可能在一天有多条记录,但我们已经用DISTINCT处理了)和留存用户数(同样去重),然后计算留存率。注意:这里假设`user_activity`表可能包含重复记录(同一用户同一天多次活跃),所以使用`DISTINCT`来确保每个用户每天只计一次。用户可以根据实际表结构和需求调整字段名和表名。现在,根据指令,我们还需要生成相关问题。</think>### SQL留存率计算方法详解 留存率是分析用户粘性的重要指标,指用户在初始活跃后,在后续特定时间窗口(如次日、7日后等)仍保持活跃的比例。下面详细介绍SQL计算留存率的方法和示例: #### 基本计算原理 留存率 = 留存用户数 / 初始用户数 - **初始用户**:在起始日(T日)活跃的用户 - **留存用户**:在起始日活跃且目标日(如T+1日)也活跃的用户 - 核心操作:通过自连接或窗口函数关联用户在不同日期的活跃状态 #### 次日留存率计算示例 ```sql SELECT q1.date AS start_date, COUNT(DISTINCT q1.user_id) AS initial_users, COUNT(DISTINCT q2.user_id) AS retained_users, ROUND(COUNT(DISTINCT q2.user_id) * 1.0 / COUNT(DISTINCT q1.user_id), 2) AS retention_rate FROM ( SELECT DISTINCT user_id, date FROM user_activity ) q1 LEFT JOIN ( SELECT DISTINCT user_id, date FROM user_activity ) q2 ON q1.user_id = q2.user_id AND q2.date = DATE_ADD(q1.date, INTERVAL 1 DAY) GROUP BY q1.date; ``` **关键步骤解析**: 1. 子查询`q1`:获取每日活跃用户(去重) 2. 子查询`q2`:同样获取活跃用户(去重) 3. `LEFT JOIN`关联条件: - `q1.user_id = q2.user_id`:匹配同一用户 - `q2.date = DATE_ADD(q1.date, INTERVAL 1 DAY)`:次日留存检查 4. 分组统计:按起始日期计算初始用户数和留存用户数 5. 留存率计算:`留存用户数/初始用户数`(乘以1.0避免整数除法) #### 多日留存率计算(如7日留存) ```sql SELECT start_date, COUNT(DISTINCT CASE WHEN diff = 1 THEN user_id END) AS day1_retained, COUNT(DISTINCT CASE WHEN diff = 7 THEN user_id END) AS day7_retained FROM ( SELECT a.user_id, first_active AS start_date, DATEDIFF(activity_date, first_active) AS diff FROM ( SELECT user_id, MIN(activity_date) AS first_active FROM user_activity GROUP BY user_id ) a JOIN user_activity b ON a.user_id = b.user_id WHERE b.activity_date BETWEEN first_active AND DATE_ADD(first_active, INTERVAL 7 DAY) ) t GROUP BY start_date; ``` #### 优化建议 1. **使用窗口函数**(更高效): ```sql WITH first_login AS ( SELECT user_id, MIN(activity_date) OVER(PARTITION BY user_id) AS first_date, activity_date FROM user_activity ) SELECT first_date AS start_date, AVG(CASE WHEN activity_date = DATE_ADD(first_date, INTERVAL 1 DAY) THEN 1.0 ELSE 0 END) AS day1_retention FROM first_login GROUP BY first_date; ``` 2. **处理边界情况**: - 排除最近日期的数据(避免留存窗口不完整) - 处理同用户多设备的情况(根据业务需求确定是否去重) - 考虑时区差异(使用`CONVERT_TZ()`统一时间) 3. **性能优化**: - 在`user_id`和`date`字段创建联合索引 - 使用临时表存储中间结果 - 分区大表按日期分区 > 示例数据表示例: > | user_id | activity_date | > |---------|---------------| > | 001 | 2023-01-01 | > | 001 | 2023-01-02 | > | 002 | 2023-01-01 | **计算结果**: | start_date | initial_users | retained_users | retention_rate | |------------|---------------|----------------|----------------| | 2023-01-01 | 2 | 1 | 0.50 | [^1]: 留存率计算通常用于分析用户在使用某个产品或服务后的留存情况 [^2]: 通过自关联计算留存率SQL中的核心实现方法 [^3]: 使用WITH子句创建临时表可提高查询可读性 [^4]: LEFT JOIN配合日期计算是实现留存分析的通用模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值