复购率必须用窗口函数标记购买顺序再聚合,否则会因忽略时间逻辑而误判;正确做法是先用row_number()按用户和时间排序标出首购与复购,再分层聚合统计。

复购率不能只靠 GROUP BY 一行搞定,必须先用窗口函数标记用户行为顺序,再分组聚合——否则会把“首购+复购”混在一起算错分母。
GROUP BY 用户ID 直接 COUNT 订单数会漏掉时间逻辑
常见错误是写成:SELECT user_id, COUNT(*) AS order_count FROM orders GROUP BY user_id,然后用 COUNT(CASE WHEN order_count > 1 THEN 1 END) / COUNT(*) 算比例。这看似合理,但问题在于:它把所有订单不加区分地统计,无法识别“是否在首次购买之后又买了”。比如某用户 2026-06-01 首购、2026-01-01 补单,按这个逻辑仍算作复购,实际是倒置时间顺序。
- 必须先按
user_id分区、按order_date排序,用ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_date)标出每笔订单是第几次购买 -
purchase_rank = 1才是首次购买;只有purchase_rank > 1的记录才构成有效复购行为 - 后续的
GROUP BY是对用户维度做二次聚合,不是对原始订单表直接分组
按月统计复购率时,GROUP BY 要套两层:先标行为,再分时间
如果要算“2026年5月支付用户的复购率”,不能只看5月内的订单次数——得查这些用户在5月之前是否买过。正确做法是:先取出5月支付成功的用户集合,再关联他们历史全部订单(含5月前),最后判断其中有多少人在5月前至少买过1次。
- 内层 CTE 用
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_date)标记所有购买序号 - 中层用
MAX(CASE WHEN purchase_rank = 1 THEN order_date END)提取每个用户的首购日期 - 外层
GROUP BY DATE_FORMAT(order_date, '%Y-%m'),再用COUNT(DISTINCT CASE WHEN first_order_date 统计当月复购用户数 - 注意:
first_order_date必须来自全量历史,不能只截取当月数据
用 LAG 函数判断“7日内复购”要小心 NULL 和跨月边界
想算七日复购率,有人直接写 LAG(order_date, 1) OVER (PARTITION BY user_id ORDER BY order_date),然后 order_date - LAG(...) 。这在多数情况下可行,但容易踩两个坑:
-
LAG返回 NULL 时,order_date - NULL整行结果变 NULL,导致该用户被完全过滤——需用COALESCE(LAG(...), '1970-01-01')填充兜底值 - 若用户在 2026-05-29 和 2026-06-02 各下一单,差值是4天,但若数据库用的是
DAYOFYEAR或未指定时区,可能因跨月计算出错;推荐统一转为UNIX_TIMESTAMP或用DATEDIFF - 仅用
LAG(..., 1)只能判断相邻两次购买,漏掉“第一次→第三次在7天内”的情况;真要严谨,得用自连接或LEAD配合多偏移量
真正难的不是写 GROUP BY,而是定义清楚“复购”——是同一用户任意两次购买间隔≤7天?还是必须在首购后7天内再次购买?前者用 LAG 就够,后者必须先锚定首购时间点。业务口径没对齐,SQL 写得再漂亮也没法跑出可信结果。











