在oracle示例数据库scott下执行
select empno,ename,job,sal,deptno from emp order by deptno,job;
--行转列
--现在查询各部门各工种的总薪水
select deptno, job, sum(sal) total_sal from emp group by deptno, job order by 1, 2;
--但是这样不直观,如果能够把每个工种作为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;
select deptno, sum(case when job='PRESIDENT' then sal else 0 end) as PRESIDENT_SAL, sum(case when job='MANAGER' then sal else 0 end) as MANAGER_SAL, sum(case when job='ANALYST' then sal else 0 end) as ANALYST_SAL, sum(case when job='CLERK' then sal else 0 end) as CLERK_SAL, sum(case when job='SALESMAN' then sal else 0 end) as SALESMAN_SAL from emp group by deptno order by 1;
--如果要在变回前面的结果,需要用到笛卡尔乘积,一行变五行,然后利用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;
--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 ( ---IN后面的值转化为列名 实际转换:sum(case when job='PRESIDENT' then sal else 0 end) as PRESIDENT_SAL 'PRESIDENT' as PRESIDENT_SAL, 'MANAGER' as MANAGER_SAL, 'ANALYST' as ANALYST_SAL, 'CLERK' as CLERK_SAL, 'SALESMAN' as SALESMAN_SAL ) ) order by 1;
--实际上,oracle对pivot子句中出现的列以外的列做了一个隐式的group by.
--现在,如果想要再结果中增加1列,显示部门的薪水总合,可以这么做
select * from (select deptno, sum(sal) over (partition by deptno) SAL_TOTAL, job, sal from emp) pivot( sum(sal) as SAL_TOTAL for job in ( 'PRESIDENT' as PRESIDENT, 'MANAGER' as MANAGER, 'ANALYST' as ANALYST, 'CLERK' as CLERK, 'SALESMAN' as SALESMAN ) ) order by 1;
--2点说明,
--1)oracle对pivot子句中出现的列以外的列,也就是deptno和SAL_TOTAL做了隐式的group by.
-- 这里用了分析函数,对于每个deptno,SAL_TOTAL是唯一的,所以group by的结果还是3行。
select * from (select deptno, job, sal from emp) pivot( sum(sal) as SAL_TOTAL, count(sal) as EMP_TOTAL for job in ( --实际转换:sum(case when job='CLERK' then sal else 0 end) as CLERK_SAL_TOTAL --count(case when job='CLERK' then sale end) as CLERK_EMP_TOTAL 'CLERK' as CLERK, 'SALESMAN' as SALESMAN ) ) order by 1;
-2)oracle会拼接列名 = for字句中别名+聚合函数别名,比如'PRESIDENT'+'_'+'SAL_TOTAL'。
--可以指定多个聚合函数,例如统计薪水总合和人数总合:
select deptno, sum(case when job='CLERK' then sal else 0 end) as CLERK_SAL_TOTAL, count(case when job='CLERK' then sal end) as CLERK_EMP_TOTAL, sum(case when job='SALESMAN' then sal else 0 end) as SALESMAN_SAL_TOTAL, count(case when job='SALESMAN' then sal end) as SALESMAN_EMP_TOTAL from emp group by deptno order by 1;
--for子句可以指定多列,
--为此,先给emp表追加1列rank,取值为'A','B',
alter table emp add (rank varchar2(1) default('A')); update emp set rank=decode(mod(rownum, 2), 0, 'B', rank); select deptno, job, rank, count(sal) as EMP_TOTAL from emp group by deptno, job, rank order by 1, 2;
--现在,想统计SALESMAN和CLERK的员工中,rank A和rank B各自的人数
select * from (select deptno, job, rank from emp) pivot( count(rank) as EMP_TOTAL for (job, rank) in ( --实际转换:sum(case when job='SALESMAN' and rank='A' then count(rank) else 0 end) as SALESMAN_A ('SALESMAN', 'A') as SALESMAN_A, ('SALESMAN', 'B') as SALESMAN_B, ('CLERK', 'A') as CLERK_A, ('CLERK', 'B') as CLERK_B ) ) order by 1;
--列转行
--unpivot的语法:
SELECT .... FROM <table-expr> UNPIVOT [include nulls|exclude nulls] ( (<column>) FOR <pivot-column> IN (<value1>, <value2>,..., <valuen>) ) AS <alias> WHERE .....
--用unpivote语句来做列到行的转换
with t as ( select * from (select deptno, job, sal from emp) pivot( sum(sal) for job in ( 'CLERK' as CLERK_SAL, 'SALESMAN' as SALESMAN_SAL ) ) ) select * from t unpivot( SAL_TOTAL for JOB in ( --以JOB为列名,存放IN后面的值,SAL_TOTAL为列 CLERK_SAL as 'CLERK', SALESMAN_SAL as 'SALESMAN' ) ) order by 1,2;
---如果加上include nulls子句
with t as ( select * from (select deptno, job, sal from emp) pivot( sum(sal) for job in ( 'CLERK' as CLERK_SAL, 'SALESMAN' as SALESMAN_SAL ) ) ) select * from t unpivot include nulls( SAL_TOTAL for JOB in ( CLERK_SAL as 'CLERK', SALESMAN_SAL as 'SALESMAN' ) ) order by 1,2;
--可以指定多个pivot-column
with t as ( select * from (select deptno, job, sal from emp) pivot( sum(sal) as SAL_TOTAL, count(sal) as EMP_TOTAL for job in ( 'CLERK' as CLERK, 'SALESMAN' as SALESMAN ) ) ) select * from t unpivot include nulls( (SAL_TOTAL, EMP_TOTAL) for JOB in ( (CLERK_SAL_TOTAL, CLERK_EMP_TOTAL) as 'CLERK', (SALESMAN_SAL_TOTAL, SALESMAN_EMP_TOTAL) as 'SALESMAN' ) ) order by 1,2;
---返回XML格式数据
select * from (select deptno, job, sal from emp) pivot XML( sum(sal) for job in (ANY) ) order by 1;