不能只用GROUP BY做漏斗,必须用CASE WHEN+SUM+用户去重,否则各环节人数分母不一致,转化率毫无意义;因为GROUP BY event_type是独立统计每类事件的去重用户数,无法保证后环节用户属于前环节用户子集,违背漏斗“分子必须是分母子集”的核心约束。

不能只用 GROUP BY 做漏斗,必须用 CASE WHEN + SUM + 用户去重,否则各环节人数分母不一致,转化率毫无意义。
为什么 GROUP BY event_type 算不出正确转化率
直接写 SELECT event_type, COUNT(DISTINCT user_id) FROM events GROUP BY event_type 看似能出各环节人数,但结果是“各自独立统计”:比如 view 有 1000 人,add_to_cart 有 800 人,这 800 人里可能只有 600 人来自那 1000 个 view 用户——你根本不知道分母是谁。
漏斗的核心约束是:后一步的分子,必须是前一步用户的子集。这不是分组问题,是同一用户池上的多条件判别问题。
- 常见错误现象:
add_to_cart人数 >view人数(说明没去重,或没限定用户池) - 真实场景中,一个用户可能多次
view,但只应算作“1 个漏斗起点用户” -
GROUP BY本身无法在一行内表达“这个用户既满足 step1 又满足 step2”
正确写法:单次扫描 + CASE WHEN + SUM + 去重子查询
核心逻辑是:先按 user_id 去重得到干净用户池,再对每个用户判断其是否达成各环节,最后汇总计数。
示例(PostgreSQL / MySQL 8.0+):
SELECT
SUM(CASE WHEN event_type = 'view' THEN 1 ELSE 0 END) AS uv_view,
SUM(CASE WHEN event_type = 'add_to_cart' THEN 1 ELSE 0 END) AS uv_cart,
SUM(CASE WHEN event_type = 'purchase' THEN 1 ELSE 0 END) AS uv_purchase,
ROUND(100.0 * uv_cart / NULLIF(uv_view, 0), 2) AS rate_view_to_cart,
ROUND(100.0 * uv_purchase / NULLIF(uv_cart, 0), 2) AS rate_cart_to_purchase
FROM (
SELECT DISTINCT user_id, event_type
FROM events
WHERE user_id IS NOT NULL
AND event_type IN ('view', 'add_to_cart', 'purchase')
) t;-
ELSE 0必须显式写出,否则SUM会跳过NULL,导致总数偏小 -
NULLIF(uv_view, 0)是防除零的关键,漏掉会导致整条 SQL 报错 - Hive 不支持
DISTINCT ON,得改用ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_time) = 1取首行 - 如果原始表中
event_type有拼写错误(如'add_to_cart '带空格),CASE WHEN会完全匹配失败,务必先TRIM()或清洗
当漏斗依赖时间顺序时,CASE WHEN 就不够用了
纯 CASE WHEN 只认“有没有”,不认“谁先谁后”。比如用户先 purchase 再 view,也会被同时计入两列,造成 uv_purchase > uv_view 的假象。
这时必须引入时序控制:
- 稳妥做法:先用子查询为每个用户提取最早/最晚各事件时间,再用条件比较,例如:
MIN(CASE WHEN event_type = 'view' THEN event_time END) < MIN(CASE WHEN event_type = 'add_to_cart' THEN event_time END) - ClickHouse 用户直接用
windowFunnel(3600)(event_time, event_type = 'view', event_type = 'add_to_cart', event_type = 'purchase'),它内置了排序、窗口、链式匹配 - MySQL / PostgreSQL 中大表慎用
LAG(),因为OVER (PARTITION BY user_id ORDER BY event_time)在百万级数据上极易 OOM 或超时
真正难的不是写出第一版 SQL,而是确认每一步的 user_id 是否来自同一逻辑口径——注册时间?首次行为时间?是否排除测试账号?这些细节一旦错位,后面所有转化率都是幻觉。

















