用UNION实现横转纵是因为兼容性强,所有标准SQL数据库都支持,而UNPIVOT仅部分数据库原生支持;基础写法为多个SELECT用UNION ALL拼接,每行对应一列转置,需保证字段数、类型、顺序一致,并用ORDER BY排序。

SQL中“横转纵”通常指将宽表结构(多列代表不同维度或指标)转换为长表结构(一列存维度名,一列存对应值)。用 UNION 实现是一种兼容性强、逻辑清晰的手法,尤其适用于不支持 UNPIVOT(如MySQL早期版本)或需跨数据库移植的场景。
为什么用 UNION 而不是 UNPIVOT?
UNPIVOT 是 SQL Server、Oracle 等部分数据库原生支持的语法,简洁高效;但 MySQL(8.0 以前)、SQLite、某些旧版 PostgreSQL 并不支持。UNION 方式手动拼接每一列的“转置行”,写法直观、所有标准 SQL 数据库都支持,调试也更可控。
基础写法:一行一列转成一行两列
假设有一张销售表 sales,含字段:year, q1, q2, q3, q4,想转成 year, quarter, amount 三列:
(注意:各 UNION 子句字段数、类型、顺序必须一致)
SELECT year, 'Q1' AS quarter, q1 AS amount FROM sales UNION ALL SELECT year, 'Q2', q2 FROM sales UNION ALL SELECT year, 'Q3', q3 FROM sales UNION ALL SELECT year, 'Q4', q4 FROM sales ORDER BY year, quarter;
- 用
UNION ALL(非UNION)避免去重开销,除非真有重复数据要剔除 - 字符串字面量(如
'Q1')作为新列值,明确标识原始列来源 - 别名统一(如
AS quarter)只需在第一个子句写,后续子句按位置匹配
处理 NULL 和空值的常见技巧
若原始列含 NULL,转置后仍为 NULL,但有时需过滤或替换:
- 跳过 NULL 行:在每个子句加
WHERE q1 IS NOT NULL - 统一补默认值:用
COALESCE(q1, 0)或IFNULL(q1, 0)(依数据库而定) - 区分空字符串和 NULL:MySQL 中可写
NULLIF(q1, '')先转空串为 NULL,再统一处理
扩展用法:多字段同时横转纵
若还有 q1_cost, q2_cost 等成本列,想一并转出,可组合多个 UNION 块,或用派生表 + JOIN —— 但更推荐分步清晰写法:
先转销量,再转成本,最后 UNION ALL 合并,并用额外字段标记指标类型:
-- 销量部分 SELECT year, 'Q1' AS quarter, q1 AS value, 'sales' AS metric FROM sales UNION ALL SELECT year, 'Q2', q2, 'sales' FROM sales -- 成本部分 UNION ALL SELECT year, 'Q1', q1_cost, 'cost' FROM sales UNION ALL SELECT year, 'Q2', q2_cost, 'cost' FROM sales ORDER BY year, quarter, metric;
这样结果含 metric 列,便于后续按指标聚合或透视,逻辑不耦合,易维护。










