首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

SQL 計算每月會員流失數量:基於 6 個月無消費判定的輔助表與窗口函數方案

     編輯:程式設計疑難解答

業務定義

  • 數據表:`PDetail` (年月 `DateValue`、會員 ID `UserId`、消費日期 `CostValue`),所有字段 `VARCHAR(8)` 格式 `YYYYMM`。
  • 規則:會員在某月份最後一次消費後 **連續 6 個月無任何消費記錄**,則在 **第 6 個月結束時 (即第 7 個月首) 判定為該月流失**。
  • 輸出:每月 `YYYYMM` 及該月流失會員數。

核心邏輯:輔助日期表 + 消費截止日期計算 + 聚合統計

1. 構建輔助年月表 `YearMonth`

包含所有需統計的月份 (如 201001~201109),保證無數據月份也能輸出 0。

2. 計算每筆消費的「應再次消費截止日期」

-- 每筆消費記錄對應的流失判定月份
-- 消費日期 CostValue (如 201001) + 6個月 = NeedCostDate (201007)
-- 會員必須在 NeedCostDate 所在月份之前 (含) 有後續消費,否則在 NeedCostDate 月份流失
SELECT 
    UserId,
    CostValue,
    DATE_FORMAT(DATE_ADD(STR_TO_DATE(CONCAT(CostValue,'01'),'%Y%m%d'), INTERVAL 6 MONTH), '%Y%m') AS NeedCostDate
FROM PDetail

3. 判定流失邏輯 (MySQL 8.0+ CTE 寫法)

WITH Consumption AS (
    -- 每筆消費及其流失判定月
    SELECT 
        UserId,
        CostValue,
        DATE_FORMAT(DATE_ADD(STR_TO_DATE(CONCAT(CostValue,'01'),'%Y%m%d'), INTERVAL 6 MONTH), '%Y%m') AS NeedCostDate
    FROM PDetail
),
NextConsumption AS (
    -- 關鍵:每筆消費後,同會員是否在 NeedCostDate 之前有下一筆消費
    SELECT 
        c.*,
        EXISTS (
            SELECT 1 FROM PDetail p2
            WHERE p2.UserId = c.UserId
              AND p2.CostValue > c.CostValue
              AND p2.CostValue <= c.NeedCostDate
        ) AS has_next_consumption
    FROM Consumption c
),
LostMembers AS (
    -- 無後續消費的記錄,標記為在 NeedCostDate 月份流失
    SELECT NeedCostDate AS LostMonth, UserId
    FROM NextConsumption
    WHERE has_next_consumption = FALSE
)
-- 最終彙總:左連接輔助表,保證無流失月份顯示 0
SELECT 
    ym.DateValue AS 年月,
    COUNT(lm.UserId) AS 該月會員流失數量
FROM YearMonth ym
LEFT JOIN LostMembers lm ON ym.DateValue = lm.LostMonth
GROUP BY ym.DateValue
ORDER BY 年月;

4. MySQL 5.7 兼容寫法 (子查詢/臨時表)

原答案提供的長 SQL 即為此邏輯的 5.7 兼容實現,核心步驟一致:

  1. 建輔助表 `YearMonth`。
  2. 子查詢計算 `NeedCostDate`。
  3. 關聯子查詢 `EXISTS` 判斷 6 個月內是否有消費。
  4. 篩選無後續消費記錄,按 `NeedCostDate` 分組計數。
  5. 左連接輔助表補全月份。

關鍵細節與優化

要點說明
日期運算字符串 `YYYYMM` → `STR_TO_DATE(CONCAT(...,'01'),'%Y%m%d')` 轉日期 → `DATE_ADD(..., INTERVAL 6 MONTH)` → `DATE_FORMAT(..., '%Y%m')` 回字符串。
邊界條件`CostValue > 當前消費` 且 `<= NeedCostDate`,嚴格大於當前消費月,小於等於截止月。
重複流失會員可能多次流失 (流失→回歸→再流失),邏輯基於每筆消費獨立判定,符合業務。
性能優化`PDetail(UserId, CostValue)` 建聯合索引;`YearMonth` 小表可全表掃描;大數據量考慮分區表或預計算物化視圖。
數據質量假設 `CostValue` 真實反映消費月份,無回補/修正導致邏輯誤判。

總結

會員流失統計核心是 **「以每次消費為基準,向前看 6 月無消費則流失」**。使用輔助日期表保證月份完整,通過日期運算得出流失判定月,再用 `EXISTS` 子查詢檢查後續消費存在性,最後聚合統計。MySQL 8.0+ 推薦 CTE 寫法清晰可維護;5.7 用嵌套子查詢/臨時表實現同樣邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved