SQL 統計用戶留存

問題描述

有一個用來記錄每日客戶消耗數據的表 t,它的表結構以下:mysql

字段 類型 描述
created_day Date 消耗日期
customer_id Integer 客戶ID
amount Integer 消耗金額

要求:sql

統計出頭部客戶、腰部客戶、尾部客戶在上個月(2020-06-01 ~ 2020-06-30)的留存狀況。微信

輸出結果的格式:.net

層級 客戶數量 留存數量
頭部客戶
腰部客戶
尾部客戶

數據定義:code

  • 頭部客戶:上個月消耗金額大於等於 30000 的客戶;
  • 腰部客戶:上個月消耗金額在 10000 ~ 30000(不包含 30000) 的客戶;
  • 尾部客戶:上個月消耗金額小於 10000 的客戶;
  • 留存:最近兩個月(上個月和本月)消耗金額大於 0 的客戶;
  • 時間:上個月(2020-06-01 ~ 2020-06-30)、本月(2020-07-01 ~ 2020-07-31)

解決方案

今天這個問題和我上一篇文章裏面的問題很類似,只不過這裏要求多統計一列,所以,解決的思路也差很少。get

下面我將用 CTE 來演示每一個步驟。it

第一步,計算出上個月每一個客戶的消耗金額。編譯

with t1 AS 
(SELECT 
  customer_id,
  SUM(amount) AS amount 
FROM
  t 
WHERE created_day BETWEEN '2020-06-01' 
  AND '2020-06-30' 
GROUP BY customer_id)
SELECT * FROM t1

第二步,在第一步的基礎上,統計頭部客戶、腰部客戶、尾部客戶的數量。table

t2 AS 
(SELECT 
  CASE
    WHEN amount >= 30000 
    THEN 1 
    WHEN amount >= 10000 
    THEN 2 
    ELSE 3 
  END AS customer_level,
  COUNT(*) AS customter_cnt 
FROM
  t1 
GROUP BY customer_level) 
SELECT * FROM t2

在腳本中使用代碼 一、二、3 分別表示頭部客戶、腰部客戶、尾部客戶。class

須要注意的是,在 GROUP BY 子句中使用了 SELECT 子句中的字段別名 customer_level,這種語法在其它數據中是編譯不經過的。

第三步,計算留存。根據留存的定義,只要客戶在本月中有消耗,就計入留存數。好比客戶 A,A 上個月的消耗金額是 40000,那麼 A 就是頭部客戶,假如 A 在本月的消耗金額大於 0,A 就爲【留存】貢獻了 1 。

咱們在 t1 以後插入表達式 t12t12 的腳本以下:

t12 AS 
(SELECT 
  t1.customer_id,
  t1.amount,
  IF(tmp.amount > 0, 1, 0) AS keep_state 
FROM
  t1 
  LEFT JOIN 
    (SELECT 
      customer_id,
      SUM(amount) AS amount 
    FROM
      t 
    WHERE created_day BETWEEN '2020-07-01' 
      AND '2020-07-31' 
    GROUP BY customer_id) tmp 
    ON tmp.customer_id = t1.customer_id)
SELECT * FROM t12

t12 中的左鏈接也能夠改形成標量子查詢。

完整的 SQL 實現:

with t1 AS 
(SELECT 
  customer_id,
  SUM(amount) AS amount 
FROM
  t 
WHERE created_day BETWEEN '2020-06-01' 
  AND '2020-06-30' 
GROUP BY customer_id),
t12 AS 
(SELECT 
  t1.customer_id,
  t1.amount,
  IF(tmp.amount > 0, 1, 0) AS keep_state 
FROM
  t1 
  LEFT JOIN 
    (SELECT 
      customer_id,
      SUM(amount) AS amount 
    FROM
      t 
    WHERE created_day BETWEEN '2020-07-01' 
      AND '2020-07-31' 
    GROUP BY customer_id) tmp 
    ON tmp.customer_id = t1.customer_id),
t2 AS 
(SELECT 
  CASE
    WHEN amount >= 30000 
    THEN 1 
    WHEN amount >= 10000 
    THEN 2 
    ELSE 3 
  END AS customer_level,
  COUNT(*) AS customter_cnt,
  SUM(keep_state) AS keep_cnt 
FROM
  t12 
GROUP BY customer_level) 

SELECT 
  CASE
    customer_level 
    WHEN 1 
    THEN '頭部客戶' 
    WHEN 2 
    THEN '腰部客戶' 
    ELSE '尾部客戶' 
  END AS '層級',
  customter_cnt AS '客戶數量',
  keep_cnt AS '留存數量' 
FROM
  t2 
ORDER BY customer_level

感興趣的朋友能夠嘗試不使用左鏈接或者標量子查詢的寫法,而是隻查一次 t 表就能實現需求。

本文分享自微信公衆號 - SQL實現(gh_684ee9235a26)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索