首先准备如下表格
tony@ORA11GR2> select empno,ename,job,sal,deptno from emp
2 order by deptno,job;
EMPNO ENAME JOB SAL DEPTNO
---------- -------------------- ------------------ ---------- ----------
7934 MILLER CLERK 1300 10
7782 CLARK MANAGER 2450 10
7839 KING PRESIDENT 5000 10
7788 SCOTT ANALYST 3000 20
7902 FORD ANALYST 3000 20
7876 ADAMS CLERK 1100 20
7369 SMITH CLERK 800 20
7566 JONES MANAGER 2975 20
7900 JAMES CLERK 950 30
7698 BLAKE MANAGER 2850 30
7654 MARTIN SALESMAN 1250 30
7521 WARD SALESMAN 1250 30
7499 ALLEN SALESMAN 1600 30
7844 TURNER SALESMAN 1500 30
现在查询各部门各工种的总薪水,
tony@ORA11GR2> select deptno, job, sum(sal) total_sal from emp
2 group by deptno, job order by 1, 2;
DEPTNO JOB TOTAL_SAL
---------- ------------------ ----------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
20 ANALYST 6000
20 CLERK 1900
20 MANAGER 2975
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
但是这样不直观,如果能够把每个工种作为1列显示就会更一目了然.
这就是需要行转列。
在11g之前,需要一点技巧,利用decode函数才能完成这个目标。
select deptno,
sum(decode(job, 'PRESIDENT', sal, 0)) as PRESIDENT_SAL,
sum(decode(job, 'MANAGER', sal, 0)) as MANAGER_SAL,
sum(decode(job, 'ANALYST', sal, 0)) as ANALYST_SAL,
sum(decode(job, 'CLERK', sal, 0)) as CLERK_SAL,
sum(decode(job, 'SALESMAN', sal, 0)) as SALESMAN_SAL
from emp group by deptno order by 1;
得到结果:
DEPTNO PRESIDENT_SAL MANAGER_SAL ANALYST_SAL CLERK_SAL SALESMAN_SAL
------ ------------- ----------- ----------- ---------- ------------
10 5000 2450 0 1300 0
20 0 2975 6000 1900 0
30 0 2850 0 950 5600
如果要在变回前面的结果,需要用到笛卡尔乘积,一行变五行,然后利用decode。例如:
with t as (
select deptno,
sum(decode(job, 'PRESIDENT', sal, 0)) as PRESIDENT_SAL,
sum(decode(job, 'MANAGER', sal, 0)) as MANAGER_SAL,
sum(decode(job, 'ANALYST', sal, 0)) as ANALYST_SAL,
sum(decode(job, 'CLERK', sal, 0)) as CLERK_SAL,
sum(decode(job, 'SALESMAN', sal, 0)) as SALESMAN_SAL
from emp group by deptno
)
select deptno,
decode(lvl, 1, 'PRESIDENT', 2, 'MANAGER', 3, 'ANALYST',
4, 'CLERK', 5, 'SALESMAN') as JOB,
decode(lvl, 1, PRESIDENT_SAL, 2, MANAGER_SAL, 3, ANALYST_SAL,
4, CLERK_SAL, 5, SALESMAN_SAL) as TOTAL_SAL
from t, (select level lvl from dual connect by level <= 5)
order by 1, 2;
得到结果:
DEPTNO JOB TOTAL_SAL
------ ------------------ ----------
10 ANALYST 0
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
10 SALESMAN 0
20 ANALYST 6000
20 CLERK 1900
20 MANAGER 2975
20 PRESIDENT 0
20 SALESMAN 0
30 ANALYST 0
30 CLERK 950
30 MANAGER 2850
30 PRESIDENT 0
30 SALESMAN 5600
11g之后,Oracle增加了pivot和unpivot语句,可以很方便的完成这个转换。
pivot
先来看看pivot的语法是
SELECT ....
FROM <table-expr>
PIVOT
(
aggregate-function(<column>)
FOR <pivot-column> IN (<value1>, <value2>,..., <valuen>)
) AS <alias>
WHERE .....
来看个例子:
select * from
(select deptno, job, sal from emp)
pivot(
sum(sal) for job in (
'PRESIDENT' as PRESIDENT_SAL,
'MANAGER' as MANAGER_SAL,
'ANALYST' as ANALYST_SAL,
'CLERK' as CLERK_SAL,
'SALESMAN' as SALESMAN_SAL
)
) order by 1;
得到结果:
DEPTNO PRESIDENT_SAL MANAGER_SAL ANALYST_SAL CLERK_SAL SALESMAN_SAL
------ ------------- ----------- ----------- ---------- ------------
10 5000 2450 1300
20 2975 6000 1900
30 2850 950 5600
实际上,oracle对pivot子句中出现的列以外的列做了一个隐式的group by.
现在,如果想要再结果中增加1列,显示部门的薪水总合,可以这么做,
tony@ORA11GR2> select * from
2 (select deptno, sum(sal) over (partition by deptno) SAL_TOTAL, job, sal from emp)
3 pivot(
4 sum(sal) as SAL_TOTAL for job in (
5 'PRESIDENT' as PRESIDENT,
6 'MANAGER' as MANAGER,
7 'ANALYST' as ANALYST,
8 'CLERK' as CLERK,
9 'SALESMAN' as SALESMAN
10 )
11 ) order by 1;
DEPTNO SAL_TOTAL PRESIDENT_SAL_TOTAL MANAGER_SAL_TOTAL ANALYST_SAL_TOTAL CLERK_SAL_TOTAL SALESMAN_SAL_TOTAL
---------- ---------- ------------------- ----------------- ----------------- --------------- ------------------
10 8750 5000 2450 1300
20 10875 2975 6000 1900
30 9400 2850 950 5600
2点说明,
1)oracle对pivot子句中出现的列以外的列,也就是deptno和SAL_TOTAL做了隐式的group by.
这里用了分析函数,对于每个deptno,SAL_TOTAL是唯一的,所以group by的结果还是3行。
2)oracle会拼接列名 = for字句中别名+聚合函数别名,比如'PRESIDENT'+'_'+'SAL_TOTAL'。
可以指定多个聚合函数,例如统计薪水总合和人数总合:
tony@ORA11GR2> select * from
2 (select deptno, job, sal from emp)
3 pivot(
4 sum(sal) as SAL_TOTAL, count(sal) as EMP_TOTAL for job in (
5 'CLERK' as CLERK,
6 'SALESMAN' as SALESMAN
7 )
8 ) order by 1;
DEPTNO CLERK_SAL_TOTAL CLERK_EMP_TOTAL SALESMAN_SAL_TOTAL SALESMAN_EMP_TOTAL
---------- --------------- --------------- ------------------ ------------------
10 1300 1 0
20 1900 2 0
30 950 1 5600 4