Oracle SQL 行列转换详解
Oracle SQL 行列转换详解
适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07
1. 概述
行列转换是常见需求[1]:
详细见:Oracle 行列转换详解。
2. 行转列(PIVOT)
2.1 PIVOT(11g+)
SELECT * FROM (
SELECT dept_id, job_id, salary FROM employees
)
PIVOT (
SUM(salary) FOR job_id IN (
'IT_PROG' AS it,
'SA_REP' AS sales,
'ST_CLERK' AS clerk
)
);
2.2 多聚合
SELECT * FROM (
SELECT dept_id, job_id, salary FROM employees
)
PIVOT (
SUM(salary) AS total, COUNT(*) AS cnt
FOR job_id IN ('IT_PROG' AS it, 'SA_REP' AS sales)
);
2.3 多列
SELECT * FROM (
SELECT dept_id, job_id, gender, salary FROM employees
)
PIVOT (
SUM(salary)
FOR (job_id, gender) IN (
('IT_PROG', 'M') AS it_m,
('IT_PROG', 'F') AS it_f,
('SA_REP', 'M') AS sales_m
)
);
2.4 XML
SELECT * FROM (
SELECT dept_id, job_id, salary FROM employees
)
PIVOT XML (
SUM(salary) FOR job_id IN (SELECT DISTINCT job_id FROM employees)
);
3. 老式行转列
3.1 CASE
SELECT dept_id,
SUM(CASE WHEN job_id = 'IT_PROG' THEN salary ELSE 0 END) AS it,
SUM(CASE WHEN job_id = 'SA_REP' THEN salary ELSE 0 END) AS sales,
SUM(CASE WHEN job_id = 'ST_CLERK' THEN salary ELSE 0 END) AS clerk
FROM employees
GROUP BY dept_id;
3.2 DECODE
SELECT dept_id,
SUM(DECODE(job_id, 'IT_PROG', salary, 0)) AS it,
SUM(DECODE(job_id, 'SA_REP', salary, 0)) AS sales,
SUM(DECODE(job_id, 'ST_CLERK', salary, 0)) AS clerk
FROM employees
GROUP BY dept_id;
4. 列转行(UNPIVOT)
4.1 UNPIVOT(11g+)
-- 列转行
CREATE TABLE sales_pivot AS
SELECT * FROM (
SELECT dept_id, q1, q2, q3, q4 FROM sales_summary
);
SELECT * FROM sales_pivot
UNPIVOT (
amount FOR quarter IN (q1, q2, q3, q4)
);
-- 结果:dept_id, quarter, amount
4.2 包含 NULL
-- 默认排除 NULL
SELECT * FROM sales_pivot
UNPIVOT INCLUDE NULLS (
amount FOR quarter IN (q1, q2, q3, q4)
);
4.3 多列
-- 多列转换
SELECT * FROM sales_pivot
UNPIVOT (
(amount, count) FOR (q_amt, q_cnt) IN (
(q1_amt, q1_cnt) AS 'Q1',
(q2_amt, q2_cnt) AS 'Q2'
)
);
5. UNION ALL
-- 老式列转行
SELECT dept_id, 'Q1' AS quarter, q1 AS amount FROM sales_summary
UNION ALL
SELECT dept_id, 'Q2' AS quarter, q2 AS amount FROM sales_summary
UNION ALL
SELECT dept_id, 'Q3' AS quarter, q3 AS amount FROM sales_summary
UNION ALL
SELECT dept_id, 'Q4' AS quarter, q4 AS amount FROM sales_summary;
6. 行转多列
6.1 案例
原始:
id | name | dept
----+------+------
1 | A | IT
2 | B | IT
3 | C | Sales
结果:
dept | names
------+------
IT | A,B
Sales | C
6.2 LISTAGG
SELECT dept, LISTAGG(name, ',') WITHIN GROUP (ORDER BY name) AS names
FROM t
GROUP BY dept;
详细见:Oracle SQL 函数大全详解。
6.3 XML
SELECT dept,
RTRIM(XMLAGG(XMLELEMENT(e, name || ',').EXTRACT('//text()')).GETSTRINGVAL(), ',') AS names
FROM t
GROUP BY dept;
7. 字符串拆分
7.1 REGEXP
-- 拆分逗号分隔
SELECT REGEXP_SUBSTR('a,b,c,d', '[^,]+', 1, LEVEL) AS val
FROM dual
CONNECT BY LEVEL <= REGEXP_COUNT('a,b,c,d', ',') + 1;
7.2 JSON
-- 12c+
SELECT t.val
FROM json_table('["a","b","c"]', '$[*]'
COLUMNS (val VARCHAR2(100) PATH '$')) t;
8. 多行合并
8.1 案例
原始:
id | value
----+------
1 | A
1 | B
1 | C
结果:
id | value
----+------
1 | A,B,C
8.2 LISTAGG
SELECT id, LISTAGG(value, ',') WITHIN GROUP (ORDER BY value) AS value
FROM t
GROUP BY id;
9. 多列合并
9.1 COALESCE
-- 多列非空
SELECT id, COALESCE(col1, col2, col3, 'N/A') AS value
FROM t;
9.2 UNPIVOT
SELECT id, value
FROM t
UNPIVOT (
value FOR col IN (col1 AS 'C1', col2 AS 'C2', col3 AS 'C3')
);
10. 12c+ 增强
10.1 LISTAGG DISTINCT
SELECT LISTAGG(DISTINCT name, ',') WITHIN GROUP (ORDER BY name) ...
10.2 LISTAGG ON OVERFLOW
SELECT LISTAGG(name, ',' ON OVERFLOW TRUNCATE '...') WITHIN GROUP (ORDER BY name) ...
10.3 PIVOT 增强
- 多列
- XML
- 动态
11. 应用场景
11.1 报表
-- 月度销售
SELECT * FROM (
SELECT TO_CHAR(sale_date, 'YYYY-MM') AS ym, region, amount FROM sales
)
PIVOT (
SUM(amount) FOR region IN ('East' AS east, 'West' AS west, 'Central' AS central)
)
ORDER BY ym;
11.2 交叉表
-- 课程成绩
SELECT * FROM (
SELECT student, course, score FROM scores
)
PIVOT (
MAX(score) FOR course IN ('Math' AS math, 'English' AS english, 'Physics' AS physics)
);
11.3 数据清洗
-- 多行变一行
SELECT user_id, LISTAGG(role, ',') WITHIN GROUP (ORDER BY role) AS roles
FROM user_roles
GROUP BY user_id;
12. 性能
12.1 PIVOT
- 内部 GROUP BY
- 索引利用
- 高效
12.2 UNION ALL
- 多次扫描
- 性能差
- 数据量大慎用
12.3 LISTAGG
- 排序
- 内存
- 大数据 OVERFLOW
13. 常见坑与排错
13.1 PIVOT 列多
- IN 子句固定
- 动态需 XML / PL/SQL
13.2 LISTAGG 超长
- VARCHAR2(4000) 限制
- 12c+ ON OVERFLOW
- CLOB
13.3 UNPIVOT NULL
- 默认排除
- INCLUDE NULLS
14. 最佳实践
- PIVOT/UNPIVOT:11g+
- CASE 老式:兼容
- LISTAGG:合并
- DISTINCT:12c+
- OVERFLOW:12c+
- XML:动态
- 索引:性能
- 测试:验证
- 性能:比较
- 文档化:说明
15. 参考资料
[1] Oracle Database SQL Language Reference 19c, “PIVOT and UNPIVOT” https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html