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; 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM