首页/数据/SQL 速查/CASE 自定义排序顺序

CASE 自定义排序顺序

按业务枚举顺序排序(如 紧急 > 高 > 普通)的常用写法:把状态映射成数字参与 ORDER BY,避免按拼音或字母排序。

SQL 速查数据更新于 2026-10-13

CASE 自定义排序是什么

按业务枚举排序(紧急 高于 高 高于 普通)不能用字母序或拼音序,通用做法是用 CASE 表达式把枚举映射成数字序号,再放进 ORDER BY 参与排序。两个要点:必须补次级排序键(时间或主键),保证同优先级内的顺序稳定,否则翻页会出现重复或漏行;映射值较多或经常变化时应改用维度表存排序号,把规则集中管理,而不是散落在多条 SQL 里。

基本信息

适用平台MySQL 8.0 / PostgreSQL / SQL Server / Oracle
原理把枚举映射为可比较的序号,交给 ORDER BY 排序
次级排序同优先级内用时间或主键列定序,保证结果稳定
替代方案冗余 sort_no 列(可建索引)、维度表关联排序、PostgreSQL 枚举类型
性能代价排序列是表达式,通常无法直接使用该列的索引用途

典型使用场景

  • 工单、告警按优先级排序(紧急优先展示)
  • 订单按业务流转顺序排列,而不是按状态字符串
  • 报表维度按自定义顺序输出(如大区按业务重要性排列)
  • 多级排序的第一级:先按自定义优先级,再按时间
  • 与分组统计配合,让输出顺序固定、便于人工核对

排序写法

排序写法
SELECT id, title, priority, created_at
FROM tickets
ORDER BY CASE priority
           WHEN 'urgent' THEN 0
           WHEN 'high'   THEN 1
           WHEN 'normal' THEN 2
           ELSE 3                  # 未覆盖的取值统一落到最后
         END,
         created_at DESC,          # 次级排序:同优先级按时间倒序
         id DESC;                  # 兜底唯一键,保证翻页稳定

-- 直接按排序号排序(冗余列方案,可走索引)
SELECT id, title FROM tickets ORDER BY sort_no, created_at DESC;

几种实现方式对比

做法优点代价
CASE 表达式不改表结构,随时调整顺序无法用列索引,映射散落在 SQL 里
冗余 sort_no 列 + 索引SQL 简单,可走索引需要维护数据一致性
维度表关联排序映射集中管理多一次连接
枚举类型(PG)定义顺序即排序规则类型定义变更成本较高
PostgreSQL 的枚举类型按 CREATE TYPE 时的定义顺序比较;MySQL 的 ENUM 列排序按内部编号(也是定义顺序)进行,两者都能把定义顺序直接当成排序规则,差别在新增取值时需要改 DDL 而不是改 SQL。
排序列套了 CASE 表达式后基本无法走索引,高频列表页每次都要计算并排序;数据量大时建议冗余 sort_no 列并建索引,把表达式排序换成索引排序。

易错点

  • 只写 CASE 不写次级排序键,同优先级行顺序不稳定,翻页出现重复或漏行
  • CASE 漏掉新上线的枚举值:未写 ELSE 时映射为 NULL,MySQL 升序排在最前、PostgreSQL 排在最后,两库表现还不一致
  • 映射规则写死在多条 SQL 里,新增状态时总有几处漏改
  • 高频列表页直接排序 CASE 表达式而不建辅助列,排序开销随数据量增长

数据来源与参考

  • MySQL 8.0 Reference Manual · ORDER BY Optimization
  • PostgreSQL Documentation · Sorting Rows
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌