管理杂谈OA答疑ERP答疑教程搜索

CASE WHEN,90% 的人只用了第一种写法


你写了很多 SQL,用 CASE WHEN 的方式,大概只有两种:

CASE WHEN col = 'A' THEN '类目A'
     WHEN
 col = 'B' THEN '类目B'
     ELSE
 '其他' END

然后就没了。

如果是这样,你可能低估了 CASE WHEN 功力。


01 UNION ALL 替代:三遍扫描 → 一遍扫描

先看一个场景。

业务方要一张报表,按用户等级分层看数据:

-- 等级分布:高中低三个用户群
SELECT
 '高活跃用户' AS user_group, COUNT(*) AS cnt
FROM
 users WHERE activity_score >= 80
UNION
 ALL
SELECT
 '中活跃用户' AS user_group, COUNT(*) AS cnt
FROM
 users WHERE activity_score BETWEEN 50 AND 79
UNION
 ALL
SELECT
 '低活跃用户' AS user_group, COUNT(*) AS cnt
FROM
 users WHERE activity_score < 50;

三个 UNION ALL,三次全表扫描。假设 users 表 1000 万行,前者扫三遍,后者只扫一遍。

用 CASE WHEN,一遍搞定:

SELECT
    CASE
 WHEN activity_score >= 80   THEN '高活跃用户'
         WHEN
 activity_score >= 50   THEN '中活跃用户'
         ELSE
  '低活跃用户' END AS user_group,
    COUNT
(*) AS cnt
FROM
 users
GROUP
 BY 1;

这就是 CASE WHEN 在 GROUP BY 里的威力——把原本 N 个 UNION ALL 的写法,收成一次扫描加分组。


02 把"不重要"的 N 个值归为"其他"

你做用户地域分析,城市有 50 个,但业务上只关心前 5 个,其他统一归为"其他城市":

SELECT
    CASE
 WHEN city IN ('北京', '上海', '深圳', '广州', '杭州')
         THEN
 city
         ELSE
 '其他城市' END AS city_group,
    COUNT
(*) AS user_cnt
FROM
 users
GROUP
 BY city_group
ORDER
 BY user_cnt DESC;

进阶——用 CASE WHEN 控制 ORDER BY 排序顺序:

SELECT
    CASE
 WHEN city IN ('北京', '上海', '深圳', '广州', '杭州')
         THEN
 city
         ELSE
 '其他城市' END AS city_group,
    COUNT
(*) AS user_cnt
FROM
 users
GROUP
 BY city_group
ORDER
 BY
    CASE
 WHEN city_group = '其他城市' THEN 1 ELSE 0 END,
    user_cnt DESC;

"其他城市"永远排最后。


03 行列转换:把行变成列,做透视表

这是 CASE WHEN 在分析场景里最有价值的用法,没有之一。

看场景:一张订单表,每行是一个用户一个月的消费记录。老板要看每个用户 1 月、2 月、3 月分别花了多少钱,列对齐——这是行转列。

一条 SQL:

SELECT
    user_id,
    SUM
(CASE WHEN month = '2026-01' THEN spend ELSE 0 END) AS jan_spend,
    SUM
(CASE WHEN month = '2026-02' THEN spend ELSE 0 END) AS feb_spend,
    SUM
(CASE WHEN month = '2026-03' THEN spend ELSE 0 END) AS mar_spend,
    SUM
(spend) AS total_spend
FROM
 monthly_spend
WHERE
 month IN ('2026-01', '2026-02', '2026-03')
GROUP
 BY user_id;

结果:

user_id
jan_spend
feb_spend
mar_spend
total_spend
001
500
800
300
1600
002
200
600
0
800

原理:每一列的 SUM 只累加 CASE WHEN 条件为真的那一行,其他行贡献 0——每列对应一个月,列对齐。

进阶:加一个维度(地区),只需 GROUP BY 多写一个字段:

SELECT
    region,
    user_id,
    SUM
(CASE WHEN month = '2026-01' THEN spend ELSE 0 END) AS jan,
    SUM
(CASE WHEN month = '2026-02' THEN spend ELSE 0 END) AS feb,
    SUM
(CASE WHEN month = '2026-03' THEN spend ELSE 0 END) AS mar
FROM
 monthly_spend
WHERE
 month IN ('2026-01', '2026-02', '2026-03')
GROUP
 BY region, user_id
ORDER
 BY region, total_spend DESC;

04 条件聚合:一个 SELECT 同时出多个条件下的指标

这是分析师用得最多、但教程写得最少的一个用法。

场景:同时看"付费用户数"、"免费用户数"、"付费金额"——四个指标,正常写法是四个子查询。

CASE WHEN + 聚合函数,一遍搞定:

SELECT
    region,
    -- 付费用户数

    COUNT
(DISTINCT CASE WHEN is_paid = 1 THEN user_id END) AS paid_user_cnt,
    -- 免费用户数

    COUNT
(DISTINCT CASE WHEN is_paid = 0 THEN user_id END) AS free_user_cnt,
    -- 付费金额

    SUM
(CASE WHEN is_paid = 1 THEN amount ELSE 0 END) AS paid_amount,
    -- 免费引导金额

    SUM
(CASE WHEN is_paid = 0 THEN guide_amount ELSE 0 END) AS free_guide_amount
FROM
 orders
GROUP
 BY region;

注意:CASE WHEN 写在 COUNT(DISTINCT ...) 里面,相当于先过滤再计数。COUNT(col) 忽略 NULL,所以 COUNT 场景下 ELSE NULL 跟不写 ELSE 效果一样。

但 SUM 必须写 ELSE 0——因为 SUM 遇到 NULL 是不累加的,和 ELSE 0 效果一样,但可读性差,容易漏:

-- ❌ 隐患:SUM 忘了写 ELSE,返回结果看起来对但逻辑不清晰
SELECT
 SUM(CASE WHEN is_paid = 1 THEN amount END) AS paid_amount FROM orders;

-- ✅ 标准写法

SELECT
 SUM(CASE WHEN is_paid = 1 THEN amount ELSE 0 END) AS paid_amount FROM orders;

05 多条件嵌套分类:WHEN 里还能再套 WHEN

CASE WHEN 可以嵌套,用来处理两个维度交叉的分类。

场景:给用户打标签,消费频次(高/中/低)× 客单价(高/低),组合成 6 类用户:

SELECT
    user_id,
    CASE

        WHEN
 order_cnt >= 10 AND avg_order_amount >= 500 THEN '高频高价值'
        WHEN
 order_cnt >= 10 AND avg_order_amount < 500  THEN '高频低价值'
        WHEN
 order_cnt >= 3  AND avg_order_amount >= 500 THEN '中频高价值'
        WHEN
 order_cnt >= 3  AND avg_order_amount < 500  THEN '中频低价值'
        WHEN
 avg_order_amount >= 500 THEN '低频高价值'
        ELSE
  '低频低价值'
    END
 AS user_segment
FROM
 user_stats;

条件顺序很重要。 上面把 order_cnt >= 10 的判断放在前面先匹配,如果反过来写,低频高价值用户会被优先匹配为"低频高价值",而实际上如果他同时满足 order_cnt >= 3 应该归入"中频高价值"。


06 综合实战:各城市月度消费趋势表

把前面所有用法串在一起,做一张真实业务里常用的报表:

WITH city_monthly AS (
    SELECT

        CASE

            WHEN
 city IN ('北京', '上海', '深圳', '广州', '杭州')
            THEN
 city
            ELSE
 '其他城市' END AS city_group,
        LEFT
(order_month, 7) AS month,
        SUM
(order_amount) AS monthly_amount
    FROM
 orders
    WHERE
 LEFT(order_month, 7) >= '2026-01'
      AND
 LEFT(order_month, 7) <= '2026-03'
    GROUP
 BY 1, 2
)
SELECT

    city_group,
    SUM
(CASE WHEN month = '2026-01' THEN monthly_amount ELSE 0 END) AS jan_amount,
    SUM
(CASE WHEN month = '2026-02' THEN monthly_amount ELSE 0 END) AS feb_amount,
    SUM
(CASE WHEN month = '2026-03' THEN monthly_amount ELSE 0 END) AS mar_amount,
    SUM
(monthly_amount) AS q1_total,
    ROUND(
        (SUM(CASE WHEN month = '2026-03' THEN monthly_amount ELSE 0 END)
        -
 SUM(CASE WHEN month = '2026-01' THEN monthly_amount ELSE 0 END)) * 100.0
        /
 NULLIF(SUM(CASE WHEN month = '2026-01' THEN monthly_amount ELSE 0 END), 0),
        2

    ) AS q1_growth_pct
FROM
 city_monthly
GROUP
 BY city_group
ORDER
 BY q1_total DESC;

一个 CTE + 一层 SELECT,城市归类、行转列透视、季度环比增长率,全部搞定。


07 CASE WHEN 语法速查

-- 基础语法
CASE

    WHEN
 条件1 THEN 结果1
    WHEN
 条件2 THEN 结果2
    WHEN
 条件3 AND 条件4 THEN 结果3
    ELSE
 默认结果          -- 建议永远写上
END


-- 在 GROUP BY 里

GROUP
 BY CASE WHEN ... END

-- 在聚合函数里

SUM
(CASE WHEN condition THEN col ELSE 0 END)
COUNT
(DISTINCT CASE WHEN condition THEN col END)

-- 嵌套

CASE
 WHEN 条件1 THEN
    CASE
 WHEN 子条件A THEN 'A1' ELSE 'A2' END
WHEN
 条件2 THEN 'B'
ELSE
 'C'
END

阅读原文:原文链接


更多精彩文章浏览...
点击右上角图标分享到朋友圈
官方网站:http://www.clicksun.cn
咨询热线:400-186-1886
服务邮箱:service@clicksun.cn