必须统一各CASE分支返回值类型,否则SQL Server会因隐式转换报Conversion failed错误或导致字符串字典序错排(如'10'<'2');应统一转VARCHAR并显式写ELSE。

SQL Server 用 CASE WHEN 动态排序必须统一类型
直接写 ORDER BY CASE WHEN @sort = 'id' THEN id WHEN @sort = 'name' THEN name END 会报错或排序错乱,因为 id 是 INT、name 是 VARCHAR,SQL Server 会尝试把所有分支转成同一类型,结果不是报 Conversion failed when converting the varchar value to data type int,就是字符串字典序排数字(比如 '10' )。
实操建议:
- 每个
THEN分支都显式转成同一种类型:数值字段用CONVERT(VARCHAR(50), col),日期字段用CONVERT(VARCHAR(23), col, 126) - 必须写
ELSE ''(字符串场景)或ELSE 0(数值场景),不能省略;否则默认补NULL,触发sql_variant类型推断,排序列类型不确定 - 升序降序别传
'ASC'/'DESC'字符串,改用@sort_dir BIT控制:两个并列CASE分支,一个带DESC,一个带ASC,让 SQL Server 保持原生类型不转换
MySQL 必须用 PREPARE 拼接 ORDER BY
ORDER BY @sort_col 直接报错 ERROR 1054: Unknown column '@sort_col'——MySQL 解析器在编译阶段就要求列名是字面量,运行时变量不被接受。唯一可行路径是字符串拼接 + PREPARE + EXECUTE。
实操建议:
- 字段名必须白名单校验:
CASE p_sort_col WHEN 'id' THEN 'id' WHEN 'name' THEN 'name' ELSE 'id' END赋值给中间变量,再拼进 SQL - 列名和排序方向都要反引号包裹:
CONCAT('ORDER BY `', @safe_col, '` ', @safe_dir),防关键字(如order)、空格字段出错 - 别把整个
ORDER BY当字符串参数传进来:@raw_order := 'id ASC, (SELECT @@version) -- '会执行子查询,注入风险极高
PostgreSQL 推荐多 CASE 并列 + NULLS LAST
PostgreSQL 允许不同类型的 CASE 分支隐式转换,但不写 NULLS LAST 会导致空值总排最前(默认 NULLS FIRST),业务逻辑错乱。单个大 CASE 包所有字段还会让优化器无法对任一分支走索引。
实操建议:
- 写法示例:
ORDER BY CASE WHEN $1 = 'updated_at' THEN updated_at END DESC NULLS LAST, CASE WHEN $1 = 'name' THEN name END NULLS LAST - 每个
CASE后紧跟NULLS LAST,确保空状态排最后;参数用$1占位符,不是@var,避免注入 - 别依赖单个
CASE表达式覆盖全部字段——多个并列CASE才能让优化器有机会对某个分支字段使用索引
动态排序和 WHERE 过滤别混用字符串拼接
用 WHERE status = ISNULL(@status, status) 看似简洁,但 SQL Server 无法对表达式列做索引 Seek,只能全表 Scan;而 ORDER BY 拼字符串更糟:执行计划无法缓存,每次都要重新编译,性能断崖下跌。
实操建议:
- 过滤条件用
WHERE (@status IS NULL OR status = @status),保留索引能力;若字段区分度低(如只有 3 个状态值),加OPTION (RECOMPILE)让每次执行重生成计划 -
ORDER BY和WHERE都必须避开字符串拼接——前者用白名单CASE,后者用OR + IS NULL,二者叠加后仍可能退化为Scan,需结合实际数据分布判断 - 最容易被忽略的是:当所有参数都为
NULL时,条件恒真,是否允许全表扫描?应在存储过程开头加逻辑提前退出,而不是等 SQL 执行完才发现慢

















