oracle10g没有行列转换函数的替代方法(转)

Posted 迷神图卷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了oracle10g没有行列转换函数的替代方法(转)相关的知识,希望对你有一定的参考价值。

在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; 

 

以上是关于oracle10g没有行列转换函数的替代方法(转)的主要内容,如果未能解决你的问题,请参考以下文章

Pandas行列转换的4大技巧

oracle 行列转换

Oracle 行列转换总结

oracle 10g中listagg的替代品

oracle12 listagg 与 wm_concat行列转换

在 Oracle 10g 的 SQL 过程中使用游标的任何替代方法?