avg(case when ... then 1.0 else 0.0 end) 直接计算转化率,因其自动对非null值求平均,将条件命中转为1、未命中转为0,等价于“满足条件行数/总行数”,天然规避分母为0、整数除法截断及集合错位问题。

AVG(CASE ...) 是计算转化率最简洁、最不容易出错的方式,比 COUNT(DISTINCT)/COUNT(DISTINCT) 更安全,也比 SUM(CASE ...)/COUNT(...) 更少写漏类型转换。
为什么 AVG(CASE WHEN ... THEN 1 ELSE 0 END) 能直接算转化率
AVG 自动对非 NULL 值求平均,而 CASE 表达式把“命中条件”转为 1、“不命中”转为 0,结果就等价于「满足条件的行数 / 总行数」。它天然规避了分母为 0 报错、整数除法截断、分子分母用户集合错位等问题。
- 不需要显式写
NULLIF—— 如果全都不满足条件(全是 0),AVG 返回 0.0,语义清晰 - 不需要乘
100.0—— 但如果你要百分比,加个* 100即可,AVG 本身已保证浮点运算 - 不依赖去重逻辑 —— 适合「事件级转化率」(如点击→提交表单),而非「用户级漏斗」(此时仍需
COUNT(DISTINCT))
AVG + CASE 在电商漏斗中的典型写法
假设你有一张统一行为日志表 events,含 user_id、event_type、event_time,想按天统计「访问 → 加购 → 下单」三阶转化:
SELECT DATE(event_time) AS dt, AVG(CASE WHEN event_type = 'cart_add' THEN 1.0 ELSE 0.0 END) AS cart_rate, AVG(CASE WHEN event_type = 'order_create' THEN 1.0 ELSE 0.0 END) AS order_rate, AVG(CASE WHEN event_type = 'pay_success' THEN 1.0 ELSE 0.0 END) AS pay_rate FROM events WHERE event_time >= '2026-09-01' GROUP BY DATE(event_time);
注意:这算的是「当日所有事件中,加购事件占比」,不是「访问用户里有多少人加购」。若需后者,必须先用子查询或 CTE 提取访问用户集合,再关联判断——此时 AVG 不适用,得换 COUNT(DISTINCT) 配合 CASE。
容易踩的坑:类型、NULL 和语义混淆
常见错误不是语法错,而是逻辑错:
- 写成
AVG(CASE WHEN event_type = 'pay_success' THEN 1 END)—— 缺少ELSE 0,未命中的行是NULL,AVG 会跳过它们,导致分母变小、结果虚高 - 用
AVG(CASE ... THEN 1 ELSE 0 END)去算「用户维度转化」—— 比如在未去重的订单表上直接算,一个用户多笔订单会被重复计数,结果失去业务意义 - 在 MySQL 中用
AVG(...)却忘了字段是TINYINT—— 返回类型仍是整数(如int),可能被隐式截断;稳妥做法是显式写1.0和0.0
什么时候该换用 COUNT(DISTINCT) + CASE
当你需要「在 A 类用户中,有多少人做了 B 动作」,就必须确保分子分母基于同一用户集合,且行为有先后关系:
- 访问后 7 天内下单率
- 注册用户中,次日活跃比例(
day2_active字段需与注册用户对齐) - 下单用户中,最终支付成功的比例(需通过
order_id关联支付表)
这时 AVG 无能为力,必须用 COUNT(DISTINCT CASE WHEN ... THEN user_id END) 并严格控制时间窗口和 JOIN 条件。漏掉 NULLIF 或乘 100.0,结果就会是整数 0 或 1。










