当前位置: 代码网 > it编程>数据库>MsSqlserver > SQL题目分析之计算用户的平均次日留存率

SQL题目分析之计算用户的平均次日留存率

2026年04月05日 MsSqlserver 我要评论
描述题目:现在运营想要查看用户在某天刷题后第二天还会再来刷题的留存率。请你取出相应数据。示例:question_practice_detailiddevice_idquestion_idresultd

描述

题目:现在运营想要查看用户在某天刷题后第二天还会再来刷题的留存率。请你取出相应数据。

示例:question_practice_detail

iddevice_idquestion_idresultdate
12138111wrong2021-05-03
23214112wrong2021-05-09
33214113wrong2021-06-15
46543111right2021-08-13
52315115right2021-08-13
62315116right2021-08-14
72315117wrong2021-08-15
83214112wrong2021-05-09
93214113wrong2021-08-15
106543111right2021-08-13
112315115right2021-08-13
122315116right2021-08-14
132315117wrong2021-08-15
143214112wrong2021-08-16
153214113wrong2021-08-18
166543111right2021-08-13

根据示例,你的查询应返回以下结果:

avg_ret
0.3000

题目分析

需求:计算用户在某天刷题后,第二天还会再来刷题的留存率

什么是次日留存率?次日留存率 = (第一天刷题的用户中,第二天也来刷题的用户数) / (第一天刷题的总用户数)

示例解读:根据示例数据,最终的平均留存率是 0.3000,即 30%。这意味着,在所有某天刷过题的用户中,平均有 30% 的用户会在第二天继续刷题。

关键点

  1. 用户标识:我们通过 device_id 来唯一标识一个用户。
  2. 行为日期:我们关心的是用户刷题的日期 (date)。
  3. 去重:一个用户在同一天可能刷了多道题,但我们只关心他 “是否来过”,所以需要对 (device_id, date) 进行去重。
  4. 关联:我们需要将用户第一天的行为和第二天的行为关联起来,以判断他是否留存。

解题思路

要计算留存率,我们需要明确两个集合:a. 某日活跃用户集合:在某天 date1 刷过题的用户。b. 次日留存用户集合:在集合 a 中的用户,并且在 date1 的第二天(date1 + 1天)也刷过题。

留存率就是 |b| / |a|

直接计算比较复杂,我们可以换个角度,为每一条 “某日刷题记录” 匹配一条 “次日刷题记录”,然后通过计数来求比率。

  1. 构建用户每日刷题的唯一记录:首先,我们需要一个只包含 (device_id, date) 唯一组合的数据集。
  2. 自连接匹配次日记录:将这个唯一记录数据集与自身进行左连接(left join)。连接条件是:
    • a.device_id = b.device_id (同一个用户)
    • b.date = a.date + interval 1 day (b 表的日期是 a 表日期的第二天)
  3. 计算留存率
    • 左连接的好处是,如果一个用户在第二天没有来,b.date 字段会是 null
    • 因此,count(b.date) 就等于 “第二天也来的用户数”。
    • count(a.date) 就等于 “第一天来的总用户数”。
    • 两者相除,就得到了我们想要的平均次日留存率。

分步解析最终代码

-- 外层查询:计算平均次日留存率
select count(date2)/count(date1) as avg_ret
from(
    -- 子查询 a: 为每个用户的每日刷题记录匹配次日是否也刷题
    select distinct 
        qpd.device_id,
        qpd.date as date1,  -- 用户当天刷题的日期
        uniq_id_date.date as date2  -- 用户次日是否刷题的日期(可能为null)
    from 
        question_practice_detail as qpd
    left join(
        -- 子查询 b: 获取所有用户所有刷题日期的唯一记录
        select distinct device_id, date
        from question_practice_detail
    )as uniq_id_date
    -- 左连接条件
    on qpd.device_id = uniq_id_date.device_id  -- 同一个用户
    and date_add(qpd.date, interval 1 day) = uniq_id_date.date -- 次日
)as id_last_next_code;

1. 子查询 b (uniq_id_date)

select distinct device_id, date
from question_practice_detail
  • 目标:创建一个 “用户 - 日期” 的唯一映射表。
  • 作用:这个子查询是整个逻辑的基石。它确保了我们处理的每一条记录都代表一个用户在某一天的一次独立访问行为,无论他当天刷了多少道题。
  • 结果示例(基于题目数据):
    device_iddate
    21382021-05-03
    32142021-05-09
    32142021-06-15
    65432021-08-13
    23152021-08-13
    23152021-08-14
    ......

2. 子查询 a (id_last_next_code)

select distinct 
    qpd.device_id,
    qpd.date as date1,
    uniq_id_date.date as date2
from 
    question_practice_detail as qpd
left join
    uniq_id_date
on 
    qpd.device_id = uniq_id_date.device_id
    and date_add(qpd.date, interval 1 day) = uniq_id_date.date
  • 目标:这是核心的匹配步骤。对于原始表中的每一条刷题记录(这里用 qpd 别名),我们都尝试在 uniq_id_date 表中找到该用户次日的刷题记录。
  • from question_practice_detail as qpd:我们从原始表开始,而不是从去重后的表开始。这是为了确保我们考虑到所有的 “刷题日”,即使一个用户一天刷了多道题,distinct 会保证最终每 (device_id, date1) 只留下一条记录。
  • left join uniq_id_date:使用左连接至关重要。它会保留 qpd 表中的所有记录。如果在 uniq_id_date 中找到了匹配的次日记录,date2 就会有值;如果没找到,date2 就会是 null
  • date_add(qpd.date, interval 1 day):这是一个日期函数,用于计算 qpd.date 的第二天。
  • select distinct ...:再次使用 distinct 是为了确保对于同一个用户在同一天的多次刷题行为,我们在最终的派生表中只保留一条 (device_id, date1, date2) 记录。
  • 结果示例
    device_iddate1date2
    21382021-05-03null-- 2138 在 5 月 4 日没有来
    32142021-05-09null-- 3214 在 5 月 10 日没有来
    23152021-08-132021-08-14-- 2315 在 8 月 14 日来了
    23152021-08-142021-08-15-- 2315 在 8 月 15 日来了
    23152021-08-15null-- 2315 在 8 月 16 日没有来
    .........

3. 外层查询

select count(date2)/count(date1) as avg_ret
from id_last_next_code;
  • 目标:计算最终的平均留存率。
  • count(date2):计算 id_last_next_code 表中 date2 字段不为 null 的行数。这正好等于 “次日留存的用户行为次数”。
  • count(date1):计算 id_last_next_code 表中 date1 字段不为 null 的行数。这正好等于 “第一天的总用户行为次数”。
  • count(date2)/count(date1):两者相除,得到的就是平均次日留存率。
  • 在我们的示例中,如果 count(date2) 是 3count(date1) 是 10,那么结果就是 3/10 = 0.3

总结

这道题的解法非常巧妙地运用了“自连接”“左连接”的技巧。

  1. 自连接:通过将一个去重后的 “用户 - 日期” 表与自身连接,我们能够在一条记录里同时看到一个用户在两天的行为。
  2. 左连接:通过左连接,我们确保了不会丢失任何一个 “第一天” 的用户行为记录,同时用 null 值清晰地标记出了哪些用户没有在 “第二天” 回来。
  3. 计数与除法:最后,利用 count() 函数对非空值的计数特性,轻松地计算出了分子(留存数)和分母(总活跃数),从而得到了最终的留存率。

这种模式在用户行为分析中非常常见,例如计算 7 日留存、月留存等,核心思想都是类似的。

到此这篇关于sql题目分析之计算用户的平均次日留存率的文章就介绍到这了,更多相关sql计算用户平均次日留存率内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com