在数据库开发中,我们会经常遇到行转列和列转行的情况,方法很多,比如使用CASE...WHEN...、DECODE,或者PIVOT和UNPIVOT都可以实现该功能,本篇讲解下PIVOT和UNPIVOT的使用,希望可以让读者举一反三,了解行列转换的用法。
PIVOT语法
SELECT ...
FROM ...
PIVOT [XML]
( pivot_clause
pivot_for_clause
pivot_in_clause )
WHERE ...
说明:
pivot_clause:定义将要进行聚合操作的列;
pivot_for_clause:定义将要进行分组和转换的列;
pivot_in_clause:定义在pivot_for_clause中对列的过滤,对于pivot_in_clause中每一个值进行,并转换为单个列。
pivot_clause::=
pivot_for_clause::=
pivot_in_clause::=
演示(利用Oracle的样例数据库,Schema为SCOTT):
1、首先我们产生一些基本数据,用于演示的基础;
SQL> select job,deptno ,sum(sal) as sum_sal
2 from emp
3 group by job,deptno
4 order by job,deptno;
JOB DEPTNO SUM_SAL
--------- ---------- ----------
ANALYST 20 6000
CLERK 10 1300
CLERK 20 1900
CLERK 30 950
MANAGER 10 2450
MANAGER 20 2975
MANAGER 30 2850
PRESIDENT 10 5000
SALESMAN 30 6100
9 rows selected.
2、使用WITH..as利用PIVOT对DEPTNO进行转列;
SQL> with pivot_emp as(
2 select job,deptno,sal from emp)
3 select *
4 from pivot_emp
5 pivot(
6 sum(sal)
7 for deptno
in(10 as dept_10,20 as dept_20,30 as dept_30,40 as dept_40)
8 );
JOB DEPT_10 DEPT_20 DEPT_30 DEPT_40
--------- ---------- ---------- ---------- ----------
CLERK 1300 1900 950
SALESMAN 6100
PRESIDENT 5000
MANAGER 2450 2975 2850
ANALYST 6000
3、也可以使用内联视图利用PIVOT对DEPTNO进行转列,和上面的结果相同;
SQL> select *from (select sal,deptno,job from emp)
2 pivot (
3 sum(sal)
4 for deptno in(10,20,30,40)
5 );
JOB 10 20 30 40
--------- ---------- ---------- ---------- ----------
CLERK 1300 1900 950
SALESMAN 6100
PRESIDENT 5000
MANAGER 2450 2975 2850
ANALYST 6000
SQL>
4、查询整个表,对DEPTNO进行转换;
SQL> select *from emp
2 pivot(
3 sum(sal) for deptno in (10,20,30,40)
4 );
结果省略...
5、PIVOT查询对于别名的使用;
SQL> select *
2 from (select job , deptno , sal from emp)
3 pivot(
4 sum(sal) as sum_sal
5 for deptno
6 in (10 as dept_10 , 20 , 30 , 40 as dept_40)
7 );
JOB DEPT_10_SUM_SAL 20_SUM_SAL 30_SUM_SAL DEPT_40_SUM_SAL
--------- --------------- ---------- ---------- ---------------
CLERK 1300 1900 950
SALESMAN 6100
PRESIDENT 5000
MANAGER 2450 2975 2850
ANALYST 6000
说明:pivot_clause中的聚合函数,如果设置了别名,会和pivot_in_clause中的列名或别名进行拼接,如果不设置,则只显示pivot_in_clause中的列名或别名。
6、PIVOT多行查询;
SQL> select *
2 from (select job , deptno , sal from emp)
3 pivot(
4 sum(sal) as sum_sal,
5 count(sal) as cnt
6 for deptno
7 in (10 as dept_10, 20 ,30 ,40)
8 );
JOB DEPT_10_SUM_SAL DEPT_10_CNT 20_SUM_SAL 20_CNT 30_SUM_SAL 30_CNT 40_SUM_SAL 40_CNT
--------- --------------- ----------- ---------- ---------- ---------- ---------- ---------- ----------
CLERK 1300 1 1900 2 950 1 0
SALESMAN 0 0 6100 4 0
PRESIDENT 5000 1 0 0 0
MANAGER 2450 1 2975 1 2850 1 0
ANALYST 0 6000 2 0 0
7、for in含有多列的查询;
SQL> select *
2 from (select job ,deptno, sal from emp)
3 pivot(
4 sum(sal) as sum_sal,count(1) as cnt
5 for (deptno,job)
6 in(
7 (30,'MANAGER') as d30_mgr,
8 (30,'CLERK') as d30_clerk,
9 (30,'SALESMAN') as d30_salman
10 )
11 );
D30_MGR_SUM_SAL D30_MGR_CNT D30_CLERK_SUM_SAL D30_CLERK_CNT D30_SALMAN_SUM_SAL D30_SALMAN_CNT
--------------- ----------- ----------------- ------------- ------------------ --------------
2850 1 950 1 6100 4
8、知道列的命名规则,可以在Select字段中列出要查询的列;
SQL> select d30_mgr_sum_sal, d30_mgr_cnt
2 from (select job ,deptno, sal from emp)
3 pivot(
4 sum(sal) as sum_sal,count(1) as cnt
5 for (deptno,job)
6 in(
7 (30,'MANAGER') as d30_mgr,
8 (30,'CLERK') as d30_clerk,
9 (30,'SALESMAN') as d30_salman
10 )
11 );
D30_MGR_SUM_SAL D30_MGR_CNT
--------------- -----------
2850 1
9、查看用PIVOT进行转换的执行计划,其实也对CASE...WHEN的改写而已;
SCOTT@orcl _SQL>explain plan set statement_id='pivot'
2 for
3 select *
4 from (select job,deptno,sal from emp)
5 pivot(
6 sum(sal)
7 for deptno
8 in (10 as dept_10,20 as dept_20,30 as dept_30,40 as dept_40)
9 );
Explained.
SCOTT@orcl _SQL>select *from table(dbms_xplan.display(null,'pivot','TYPICAL +PROJECTION'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1475541029
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 75 | 4 (25)| 00:00:01 |
| 1 | HASH GROUP BY PIVOT| | 5 | 75 | 4 (25)| 00:00:01 |
| 2 | TABLE ACCESS FULL | EMP | 14 | 210 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------
Column Projection Information (identified by operation id):
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
-----------------------------------------------------------
1 - (#keys=1) "JOB"[VARCHAR2,9], SUM(CASE WHEN ("DEPTNO"=10) THEN
"SAL" END )[22], SUM(CASE WHEN ("DEPTNO"=20) THEN "SAL" END )[22],
SUM(CASE WHEN ("DEPTNO"=30) THEN "SAL" END )[22], SUM(CASE WHEN
("DEPTNO"=40) THEN "SAL" END )[22]
2 - "JOB"[VARCHAR2,9], "SAL"[NUMBER,22], "DEPTNO"[NUMBER,22]
18 rows selected.
UNPIVOT语法
SELECT ...
FROM ...
UNPIVOT [INCLUDE|EXCLUDE NULLS]
( unpivot_clause
unpivot_for_clause
unpivot_in_clause )
WHERE ...
unpivot_clause::=
unpivot_in_clause::=
演示:
1、创建一个view,用于存放基础数据;
SCOTT@orcl _SQL>create view pivoted_emp as
2 select *
3 from (select job,deptno,sal from emp)
4 pivot (
5 sum(sal)
6 for deptno
7 in (10 as dept10,20 as dept20,30 as dept30,40 as dept40)
8 );
SCOTT@orcl _SQL>select *from pivoted_emp;
JOB DEPT10 DEPT20 DEPT30 DEPT40
--------- ---------- ---------- ---------- ----------
CLERK 1300 1900 950
SALESMAN 6100
PRESIDENT 5000
MANAGER 2450 2975 2850
ANALYST 6000
SCOTT@orcl _SQL>
2、利用UNPIVOT进行列转行操作;
SCOTT@orcl _SQL>select *
2 from pivoted_emp
3 unpivot (
4 sal
5 for deptno in (dept10 as 10,dept20 as 20,dept30 as 30, dept40 as 40)
6 );
JOB DEPTNO SAL
--------- ---------- ----------
CLERK 10 1300
CLERK 20 1900
CLERK 30 950
SALESMAN 30 6100
PRESIDENT 10 5000
MANAGER 10 2450
MANAGER 20 2975
MANAGER 30 2850
ANALYST 20 6000
9 rows selected.
SCOTT@orcl _SQL>
3、从上面的结果显示,不包含NULL值的JOB,如果显示出来,需要使用INCLUDE关键字;
SCOTT@orcl _SQL>select *
2 from pivoted_emp
3 unpivot include nulls(
4 sal
5 for deptno in (dept10 as 10,dept20 as 20,dept30 as 30, dept40 as 40)
6 );
JOB DEPTNO SAL
--------- ---------- ----------
CLERK 10 1300
CLERK 20 1900
CLERK 30 950
CLERK 40
SALESMAN 10
SALESMAN 20
SALESMAN 30 6100
SALESMAN 40
PRESIDENT 10 5000
PRESIDENT 20
PRESIDENT 30
JOB DEPTNO SAL
--------- ---------- ----------
PRESIDENT 40
MANAGER 10 2450
MANAGER 20 2975
MANAGER 30 2850
MANAGER 40
ANALYST 10
ANALYST 20 6000
ANALYST 30
ANALYST 40