如何通过SQL分析漏斗模型各阶段转化率?

作者:袖梨 2026-07-01
漏斗转化率不准主因是未按用户级路径判定——必须用 user_id 关联各环节交集,而非独立统计后硬除;正确做法是先按 user_id 分组,再用 MAX(CASE WHEN...) 打标各环节完成情况,并统一时间范围。

漏斗转化率不准,八成是因为直接用 COUNT(*) 或没做用户级路径判定——必须按 user_id 判断“谁走到了哪一步”,而不是数事件行数。

为什么用 COUNT(DISTINCT user_id) 还是算不准?

常见错误是把各环节独立统计后硬除:比如先算出 1000 人访问、800 人加购,就直接 800/1000=80%。问题在于——这 800 个加购的人,未必全来自那 1000 个访问用户(可能含新用户、跨天用户、重复 ID)。真正的分母必须是“上一步的分子”,即:能走到第二步的人,得先在第一步集合里。

  • 第一步(如 view_landing)分母 = 该时间窗口内去重 user_id
  • 第二步(如 add_to_cart)分母 = 同一窗口内既触发 view_landing 又触发 add_to_cartuser_id
  • 必须用 INNER JOINWHERE user_id IN (SELECT ...) 显式约束交集,不能靠时间范围“碰巧重叠”

用 MAX(CASE WHEN ...) 做用户级标记最稳

比起嵌套子查询或多次 JOIN,对每个 user_id 打标是否完成某环节更清晰、易读且数据库优化友好。核心是:先按 user_id 分组,再用条件聚合生成布尔标记。

  • 写法:MAX(CASE WHEN event_name = 'view_landing' THEN 1 ELSE 0 END) → 返回 1 表示该用户至少做过一次
  • EXISTS 子查询更易调试,也避免了 MySQL 中相关子查询性能差的问题
  • 所有环节必须统一时间范围,例如都限定 event_time >= '2026-06-16' AND event_time ,否则分母失去可比性

时间顺序和窗口限制必须显式处理

用户先支付再浏览,CASE WHEN 也会记为“完成两步”,但漏斗实际没走通。真实路径要求:后一步必须发生在前一步之后,且间隔在业务容忍窗口内(如 1 小时、7 天)。

  • LAG(event_name) OVER (PARTITION BY user_id ORDER BY event_time) 拿到上一事件类型,再 WHERE event_name = 'add_to_cart' AND prev_event = 'view_landing'
  • 如果要求“加购必须在浏览后 30 分钟内”,就得加 event_time - prev_time
  • 注意:LAG() 对每个用户首行返回 NULL,无需额外过滤;但排序必须严格按 event_time,不能只按日期

除零和整数截断是线上事故高发点

生产 SQL 里漏掉 NULLIF 或用整数除法,会导致整个结果为 NULL 或 0,而且很难被监控发现。

  • 分母必须包一层 NULLIF(denominator, 0),否则 pay_cnt / order_cnt 在没人下单时直接报错
  • 乘 100.0 而不是 100:PostgreSQL/MySQL 中 3/5 = 0,但 3*100.0/5 = 60.0
  • 如果某环节人数为 0,转化率应为 NULL 或明确标注“无数据”,而不是跳过该行

最容易被忽略的是:同一用户在单次会话中重复触发同一事件(比如多次点击注册按钮),会导致第一步人数虚高;必须确认去重逻辑是在事件层(COUNT(DISTINCT user_id))还是会话层(COUNT(DISTINCT session_id)),口径不一致,整个漏斗就失效。

相关文章

精彩推荐