SQL练手题:计算用户留存率



一条 SQL 计算用户留存率。

题目

一个用户登录的日期距离它第一天登录的日期为 N 天,则称该用户在第 N 天留存。第一天登录用户数 M 中,有 m 个用户在第 N 天有登录,则称 N 日用户留存率为: m/M 。

设用户登录日志表为:


  - Table: user_login

  | Column     | Type    |
  |------------+---------|
  | user_id    | Integer |
  | login_date | Date    |

要求用一条 SQL 计算 1,3,5,7,13,15 日的用户留存率,要求 SQL 有扩展性,以便日后新增查询 Y 日用户留存率时尽量减少代码修改量。

解答

以 PostgreSQL 写法作答:


WITH user_first_login AS (
    SELECT DISTINCT
           ulog.user_id
         , ulog.login_date
         , FIRST_VALUE(ulog.login_date)
           OVER (PARTITION BY ulog.user_id
                     ORDER BY ulog.login_date ASC)  AS first_login_date
         , ulog.login_date
         - FIRST_VALUE(ulog.login_date)
           OVER (PARTITION BY ulog.user_id
                     ORDER BY ulog.login_date ASC)  AS login_date_gap

         , COUNT(1)
           OVER (PARTITION BY ulog.login_date)      AS user_count_day0
      FROM user_login ulog
), user_retention_count AS (
     SELECT ufd.login_date
          , ufd.first_login_date
          , ufd.login_date_gap
          , COUNT(1)       AS user_count
          , ufd.user_count_day0
       FROM user_first_login ufd
      WHERE ufd.login_date_gap IN (0,1,3,5,7,13,15)
   GROUP BY ufd.login_date, ufd.first_login_date
          , ufd.login_date_gap, ufd.user_count_day0
)      SELECT urc.login_date
            , urc.login_date_gap
            , urc.user_count       AS retention_count
            , urc.user_count::float/urc2.user_count_day0::float AS retention_rate
         FROM user_retention_count urc
    LEFT JOIN user_retention_count urc2
           ON urc2.login_date = urc.first_login_date
          AND urc2.login_date_gap = 0
 ORDER BY urc.login_date, urc.login_date_gap
;

如果在子查询 user_retention_count 中不限定 where 条件,则最后得到的结果可以查任意 N 日用户留存率。

本文链接: https://paxinla.github.io/posts/2020/12/sqllian-shou-ti-ji-suan-yong-hu-liu-cun-lu.html

知识共享许可协议 本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可,欢迎转载、演绎,
但是必须保留本文的署名 Charles(包含链接),且不得用于商业目的。