子查询中不能用case when实现列转行,因其不扩展行数;真正可行的是union all拆列合并,或lateral/values构造行集,且case when仅适用于已转行数据的分类标记。

子查询里不能直接用CASE WHEN做列转行?先搞清执行顺序
SQL中CASE WHEN本身不改变行数,它只是逐行计算新值;所谓“列转行”,本质是把原表中多个列的值,变成多行单列输出——这必须靠行数扩展操作(如UNION ALL或横向展开),而不是靠CASE WHEN单点映射。子查询如果只包一层CASE WHEN,结果仍是1行N列,根本没转成行。
常见错误现象:SELECT id, (SELECT CASE WHEN type=1 THEN name END) AS val FROM t —— 这里子查询没加FROM、没关联外层,语法直接报错;即使补全,也只会返回单个标量值,无法生成多行。
真正可行的路径只有两条:
- 用
UNION ALL把各列拆成独立子查询,再合并(兼容性高,所有SQL方言都支持) - 用
LATERAL(PostgreSQL/SQL Server)或APPLY(SQL Server)配合VALUES构造行集(更简洁,但非通用)
用UNION ALL + 子查询实现安全列转行(MySQL/Oracle/SQL Server通吃)
假设原表t有id、col_a、col_b、col_c四列,想转成id、col_name、col_value三列多行结构。核心思路:每个列单独写一个子查询,用UNION ALL拼接,且每条子查询都显式带上id和列名字符串。
实操示例:
SELECT id, 'col_a' AS col_name, col_a AS col_value FROM t UNION ALL SELECT id, 'col_b', col_b FROM t UNION ALL SELECT id, 'col_c', col_c FROM t ORDER BY id, col_name;
注意点:
- 所有子查询字段数、类型必须严格一致(
col_a/col_b/col_c最好同为字符串或可隐式转为同一类型,否则MySQL可能报Illegal mix of collations) -
UNION ALL比UNION快,因为不查重;列转行场景天然无重复,必须用ALL - 别在子查询里写
WHERE过滤原始数据——要过滤,得在外层加,否则各分支结果行数不一致会导致UNION ALL出错
嵌套CASE WHEN真正该用在哪?配合行转列反向校验
很多人误以为CASE WHEN能“转行”,其实是把它用反了:CASE WHEN适合在已转成行的数据上,按条件归类聚合,比如验证列转行结果是否符合业务规则。
例如,对上面UNION ALL产出的宽表结果,你想标记哪些值属于“主名称”、哪些是“备用名称”,就可以嵌套一层:
SELECT
id,
col_name,
col_value,
CASE
WHEN col_name IN ('col_a', 'col_b') THEN 'primary'
WHEN col_name = 'col_c' THEN 'backup'
ELSE 'unknown'
END AS category
FROM (
SELECT id, 'col_a' AS col_name, col_a AS col_value FROM t
UNION ALL
SELECT id, 'col_b', col_b FROM t
UNION ALL
SELECT id, 'col_c', col_c FROM t
) t2;
关键区别:
- 这里的
CASE WHEN作用于已经扩展出的多行数据,不是试图让单行变多行 - 子查询是纯数据源,不承担逻辑判断;判断逻辑全部放在外层
SELECT中 - 如果把
CASE WHEN塞进子查询内部(比如每个UNION分支里都加),不仅冗余,还容易因分支间CASE逻辑不一致导致分类混乱
性能和可维护性陷阱:别让列转行变成定时炸弹
列数越多,UNION ALL分支越多,执行计划越难优化。10列就要写10次SELECT,扫描原表10遍——这不是设计问题,是硬伤。
真实项目里更稳妥的做法:
- 优先考虑应用层处理:读出原始行后,在Python/Java里用循环+
yield或流式构造新行,避免数据库反复扫描 - 如果必须SQL内完成,且列数固定且不多(≤5),用
UNION ALL最稳;超过5列,建议建视图封装,把UNION ALL逻辑收口,别散落在各处SQL里 - 千万别用
JSON_OBJECT或CONCAT拼字符串假装“转行”——后续没法索引、没法WHERE、没法和其他表JOIN,等于自废武功
最易被忽略的一点:列转行后,id不再唯一,GROUP BY或DISTINCT必须明确指定粒度,否则聚合结果会错得毫无征兆。










