MySQL语法的高级用法CASE WHEN

Mysql 8.4 参考文档:

https://dev.mysql.com/doc/refman/8.4/en/date-and-time-functions.html

基础用法

复制代码
-- 简单 CASE 表达式
CASE column
    WHEN value1 THEN result1
    WHEN value2 THEN result2
    ELSE default_result
END

-- 搜索 CASE 表达式
CASE
    WHEN condition1 THEN result1
    WHEN condition2 THEN result2
    ELSE default_result
END
** 1、自定义排序**
复制代码
SQL的ORDER BY 默认按字母排序,数字大小或日期先后排序,但业务需求往往不按常理出牌:
 1. 想要订单状态按【待支付--已支付--已发货--已完成】排序
 2. 想要用户等级【砖石--黄金--白银--青铜】排序
 3. 想要星期从【周一】开始排,而不是【周五】
 这就需要自定义排序--你说了算
2、核心语法:CASE WHEN + ORDER BY
复制代码
order by case 字段名
	when '值1' then 1
	when '值2' then 2
	when '值3' then 3
	else 99
end

原理:把业务含义转换为数字,数据库按数字排序,数字小的排在前面

3、应用场景
复制代码
场景1:订单状态排序
业务需求:用户想要订单列表,按【待支付--已支付--已发货--已完成--已取消】的顺序展示
select order_id,custoer_name,status from orders 
order by case status 
when '待支付' then 1
when '已支付' then 2
when '已发货' then 3
when '已完成' then 4
when '已取消' then 5
else 99
end;

场景2:星期排序(从周一开始)
select sale_date,WEEKDAY(sale_date) as weekday_num,
sum(amount) as total_sales
from sales group by sale_date 
order by case DAYOFWEEK(sale_date)
when 2 then 1 --周一
when 3 then 2 --周二
when 4 then 3 --周三
when 5 then 4 --周四
when 6 then 5 --周五
when 7 then 6 --周六
when 1 then 7 --周日
end;
4、多级自定义排序
复制代码
select order_id,status,create_time from orders order by case status when '待支付' then 1
when '已支付' then 2
when '已发货' then 3
else 99
end, create_time desc; --同状态按照创建时间排序
效果:所有【待支付】排在最前面(按时间倒序),然后是已支付,已发货
五、记得处理else
复制代码
不在CASE里的值会归到ELSE,如果不写ELSE,这些值会变成NULL,NULL在排序中会默认排最前或最后(取决于数据库),可能导致意外
数字不一定连续
THEN 1、THEN 100、THEN 500 完全可以,只要大小关系正确
order by CASE level 
when '砖石' then 10
when '黄金' then 20
when '白银' then 30
end 
-- 10<20<30,排序正确

可以组合多个CASE
order by 
	CASE type when 'VIP' then 1 else 2 end,
	case region when '华北' then 1 else 2 end

数组分组统计(行转列)

复制代码
-- 按月份统计不同状态的订单数量
SELECT 
    YEAR(order_date) AS year,
    MONTH(order_date) AS month,
    SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS completed_count,
    SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending_count,
    SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS cancelled_count,
    COUNT(*) AS total_count
FROM orders
GROUP BY YEAR(order_date), MONTH(order_date);

条件聚合计算

复制代码
-- 根据不同条件计算不同的聚合值
SELECT 
    department_id,
    AVG(CASE WHEN gender = 'M' THEN salary END) AS avg_male_salary,
    AVG(CASE WHEN gender = 'F' THEN salary END) AS avg_female_salary,
    MAX(CASE WHEN job_level = 'senior' THEN salary END) AS max_senior_salary,
    MIN(CASE WHEN job_level = 'junior' THEN salary END) AS min_junior_salary
FROM employees
GROUP BY department_id;

自定义排序

复制代码
-- 根据不同条件计算不同的聚合值
SELECT 
    department_id,
    AVG(CASE WHEN gender = 'M' THEN salary END) AS avg_male_salary,
    AVG(CASE WHEN gender = 'F' THEN salary END) AS avg_female_salary,
    MAX(CASE WHEN job_level = 'senior' THEN salary END) AS max_senior_salary,
    MIN(CASE WHEN job_level = 'junior' THEN salary END) AS min_junior_salary
FROM employees
GROUP BY department_id;

数据透视表

复制代码
-- 将行数据转换为列
SELECT 
    student_name,
    MAX(CASE WHEN subject = 'math' THEN score END) AS math_score,
    MAX(CASE WHEN subject = 'english' THEN score END) AS english_score,
    MAX(CASE WHEN subject = 'science' THEN score END) AS science_score
FROM student_scores
GROUP BY student_name;

空值处理

复制代码
-- 处理 NULL 值并提供默认值
SELECT 
    product_name,
    CASE 
        WHEN description IS NULL OR description = '' THEN 'No description available'
        ELSE description
    END AS display_description,
    COALESCE(
        CASE 
            WHEN discount_price > 0 THEN discount_price
            ELSE original_price
        END, 
        original_price
    ) AS final_price
FROM products;

update语句中的case when

复制代码
-- 批量更新不同条件的数据
UPDATE employees
SET 
    bonus = CASE 
        WHEN performance_score >= 90 THEN salary * 0.2
        WHEN performance_score >= 80 THEN salary * 0.15
        WHEN performance_score >= 70 THEN salary * 0.1
        ELSE salary * 0.05
    END,
    level = CASE 
        WHEN years_of_service >= 10 THEN 'Senior'
        WHEN years_of_service >= 5 THEN 'Mid'
        ELSE 'Junior'
    END
WHERE department_id = 1;

根据参数动态排序

复制代码
-- 根据参数动态排序
SELECT * FROM products
ORDER BY 
    CASE 
        WHEN @sort_by = 'price_asc' THEN price
    END ASC,
    CASE 
        WHEN @sort_by = 'price_desc' THEN price
    END DESC,
    CASE 
        WHEN @sort_by = 'name' THEN product_name
    END ASC;

having子句的使用

复制代码
-- 在 HAVING 中使用 CASE WHEN 进行条件过滤
SELECT 
    department_id,
    COUNT(*) AS emp_count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING 
    SUM(CASE WHEN salary > 5000 THEN 1 ELSE 0 END) > 5
    AND AVG(CASE WHEN gender = 'M' THEN salary END) > 6000;

join条件中使用

复制代码
-- 在 JOIN 条件中使用 CASE WHEN
SELECT 
    o.order_id,
    c.customer_name,
    CASE 
        WHEN o.total_amount > 1000 THEN 'Premium'
        ELSE 'Standard'
    END AS customer_tier
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
LEFT JOIN vip_members v ON 
    c.customer_id = v.customer_id 
    AND CASE 
        WHEN v.expiry_date > CURDATE() THEN 1 
        ELSE 0 
    END = 1;

嵌套case when

复制代码
-- 嵌套使用 CASE WHEN
SELECT 
    employee_name,
    CASE 
        WHEN department = 'Sales' THEN
            CASE 
                WHEN sales_volume > 100000 THEN 'Top Sales'
                WHEN sales_volume > 50000 THEN 'Good Sales'
                ELSE 'Normal Sales'
            END
        WHEN department = 'Tech' THEN
            CASE 
                WHEN projects_completed > 10 THEN 'Senior Dev'
                WHEN projects_completed > 5 THEN 'Mid Dev'
                ELSE 'Junior Dev'
            END
        ELSE 'Other'
    END AS employee_category
FROM employees;

日期时间处理

复制代码
-- 根据日期时间进行分类
SELECT 
    order_id,
    order_date,
    CASE 
        WHEN HOUR(order_time) BETWEEN 6 AND 11 THEN 'Morning'
        WHEN HOUR(order_time) BETWEEN 12 AND 17 THEN 'Afternoon'
        WHEN HOUR(order_time) BETWEEN 18 AND 23 THEN 'Evening'
        ELSE 'Night'
    END AS time_period,
    CASE DAYOFWEEK(order_date)
        WHEN 1 THEN 'Sunday'
        WHEN 7 THEN 'Saturday'
        ELSE 'Weekday'
    END AS day_type
FROM orders;

窗口函数使用

复制代码
-- 与窗口函数结合使用
SELECT 
    employee_name,
    department_id,
    salary,
    CASE 
        WHEN RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) = 1 THEN 'Highest'
        WHEN RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) <= 3 THEN 'Top 3'
        ELSE 'Normal'
    END AS salary_rank_category
FROM employees;

INSERT INTO SELECT 中使用

复制代码
-- 在插入数据时使用 CASE WHEN 转换
INSERT INTO employee_summary (emp_id, emp_name, salary_level, age_group)
SELECT 
    id,
    name,
    CASE 
        WHEN salary >= 10000 THEN 'High'
        WHEN salary >= 5000 THEN 'Medium'
        ELSE 'Low'
    END,
    CASE 
        WHEN age < 30 THEN 'Young'
        WHEN age < 50 THEN 'Middle'
        ELSE 'Senior'
    END
FROM employees;

计算字段和指标

复制代码
-- 计算复杂的业务指标
SELECT 
    DATE_FORMAT(order_date, '%Y-%m') AS month,
    COUNT(*) AS total_orders,
    SUM(total_amount) AS total_revenue,
    SUM(CASE WHEN status = 'completed' THEN total_amount ELSE 0 END) AS completed_revenue,
    SUM(CASE WHEN status = 'refunded' THEN total_amount ELSE 0 END) AS refunded_amount,
    ROUND(
        SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 
        2
    ) AS completion_rate
FROM orders
GROUP BY DATE_FORMAT(order_date, '%Y-%m');

案例

复制代码
-- 电商订单分析报表
SELECT 
    DATE_FORMAT(o.order_date, '%Y-%m-%d') AS order_day,
    COUNT(DISTINCT o.customer_id) AS unique_customers,
    COUNT(o.order_id) AS total_orders,
    SUM(o.total_amount) AS gmv,
    SUM(CASE WHEN o.status = 'paid' THEN o.total_amount ELSE 0 END) AS paid_gmv,
    SUM(CASE WHEN o.status = 'refunded' THEN o.total_amount ELSE 0 END) AS refund_gmv,
    ROUND(AVG(o.total_amount), 2) AS avg_order_value,
    SUM(CASE WHEN p.category = 'electronics' THEN 1 ELSE 0 END) AS electronics_orders,
    SUM(CASE WHEN p.category = 'clothing' THEN 1 ELSE 0 END) AS clothing_orders
FROM orders o
LEFT JOIN order_items oi ON o.order_id = oi.order_id
LEFT JOIN products p ON oi.product_id = p.product_id
GROUP BY DATE_FORMAT(o.order_date, '%Y-%m-%d')
ORDER BY order_day DESC;
相关推荐
Absurd5872 小时前
Navicat导出JSON数据为空如何解决_过滤条件与权限排查
jvm·数据库·python
m0_716430072 小时前
SQL如何高效统计分类下的多项指标_善用CASE WHEN与SUM聚合
jvm·数据库·python
m0_588758482 小时前
PHP源码运行受主板供电影响吗_供电相数重要性说明【技巧】
jvm·数据库·python
qq_413847402 小时前
如何处理MongoDB跨分片事务报错_4.2+分布式事务的限制与两阶段提交延迟
jvm·数据库·python
InfinteJustice2 小时前
HTML函数在超频CPU上更流畅吗_超频对HTML函数影响【技巧】
jvm·数据库·python
AKA__Zas2 小时前
初识 事务
java·开发语言·数据库·sql
kongba0072 小时前
2026年4月19日 kimi记忆备份
java·前端·数据库
xyyaihxl2 小时前
【SQL技术】不同数据库引擎 SQL 优化方案剖析
数据库·sql
21439652 小时前
Less如何构建CSS样式库_通过继承机制优化组件化开发
jvm·数据库·python