經常用到的透視函式(行轉列&列轉行)函式 PIVOT()&UNPIVOT
說明: 工作中經常遇到一組or一條資料按照不同型別被分成多條資料,如 一條合同的分期還款賬單按照不同型別被分成本金,利息,管理費等,
資料統計工作中,一條合同一期就會分成多條合同
如
實際工作中 常需要將同一期多條資料整合成一期並且賬單條目型別橫向顯示,即資料透視過程:
即實現
可使用oracle自帶函式 pivot() 資料透視函式;
具體過程如下
select t.*, nvl(lead(date_due) over(partition by id_credit order by num_instalment ), date'3000-01-01') as next_due from ---外層select語句,可直接操作資料透視表的結果
(select ins.num_instalment, ins.id_credit, ins.date_due, ty.name ,ins.value_instalment ,sum(ins.value_instalment) over(partition by ins.id_credit) as summary from INSTALSCHED.INSTALMENT ins join INSTALSCHED.INSTALTYPE ty on ins.type_instalment=ty.type_instalment where ins.status='a' and ins.num_instalment <1000 and ins.id_credit ='815925' --and ins.id_credit in ('815925' ,'816312') ) ---子查詢表部分, pivot所做的操作都是基於這張表裡的資料結果,
PIVOT (SUM(value_instalment) ---pivot語句中必須包含聚合函式
FOR name IN ('本金','利息') --指定需要轉換的行的值, 及轉成列後的欄位名
) t
order by ID_CREDIT,num_instalment
Reference
Oracle 11g 行列互換 pivot 和 unpivot 說明
在Oracle 11g中,Oracle 又增加了2個查詢:pivot(行轉列) 和unpivot(列轉行)
參考:http://blog.csdn.net/tianlesoftware/article/details/7060306、http://www.oracle.com/technetwork/cn/articles/11g-pivot-101924-zhs.
google 一下,網上有一篇比較詳細的文件:http://www.oracle-developer.net/display.php?id=506
pivot 列轉行
測試資料 (id,型別名稱,銷售數量),案例:根據水果的型別查詢出一條資料顯示出每種型別的銷售數量。
SQL Code
1 |
create table demo(id int,name varchar(20),nums int); ---- 建立表 |
分組查詢 (當然這是不符合查詢一條資料的要求的)
SQL Code
1 |
select name, sum(nums) nums from demo group by name |
行轉列查詢
SQL Code
1 |
select * from (select name, nums from demo) pivot (sum(nums) for name in ('蘋果'蘋果, '橘子', '葡萄', '芒果')); |
注意: pivot(聚合函式 for 列名 in(型別)) ,其中 in('') 中可以指定別名,in中還可以指定子查詢,比如 select distinct code from customers
當然也可以不使用pivot函式,等同於下列語句,只是程式碼比較長,容易理解
SQL Code
1 |
select * |
unpivot 行轉列
顧名思義就是將多列轉換成1列中去
案例:現在有一個水果表,記錄了4個季度的銷售數量,現在要將每種水果的每個季度的銷售情況用多行資料展示。
建立表和資料
SQL Code
1 |
create table Fruit(id int,name varchar(20), Q1 int, Q2 int, Q3 int, Q4 int); |
列轉行查詢
SQL Code
1 |
select id , name, jidu, xiaoshou from Fruit unpivot (xiaoshou for jidu in (q1, q2, q3, q4) ) |
注意: unpivot沒有聚合函式,xiaoshou、jidu欄位也是臨時的變數
同樣不使用unpivot也可以實現同樣的效果,只是sql語句會很長,而且執行速度效率也沒有前者高
SQL Code
1 |
select id, name ,'Q1' jidu, (select q1 from fruit where id=f.id) xiaoshou from Fruit f |
XML型別
上述pivot列轉行示例中,你已經知道了需要查詢的型別有哪些,用in()的方式包含,假設如果您不知道都有哪些值,您怎麼構建查詢呢?
pivot 操作中的另一個子句 XML 可用於解決此問題。該子句允許您以 XML 格式建立執行了 pivot 操作的輸出,在此輸出中,您可以指定一個特殊的子句 ANY 而非文字值
示例如下:
SQL Code
1 |
select * from ( |
如您所見,列 NAME_XML 是 XMLTYPE,其中根元素是 <PivotSet>。每個值以名稱-值元素對的形式表示。您可以使用任何 XML 分析器中的輸出生成更有用的輸出。
對於該xml檔案的解析,貼程式碼如下:
SQL Code
1 |
create or replace procedure ljz_pivot_xml_sp(pi_table_name varchar2, |
第一個引數為要解析xml檔案所屬資料表,第二個引數為要解析xml所存欄位,第三個引數存放解析後的資料集。
測試:
begin
ljz_pivot_xml_sp('(select * from (select deptno,sal from emp) pivot xml(sum(sal) for deptno in(any)))',
'deptno_xml',
'ljz_pivot_tmp');
end;
初學oracle xml解析,這種方法較為笨拙,一個一個迴圈列,原型如下:
select extractvalue(name_xml, '/PivotSet/item[1]/column[1]')
from (select * from (select name,nums from demo) pivot xml(sum(nums) for name in(any))) x
where existsnode(name_xml, '/PivotSet/item[1]/column[1]') = 1;
select x.*
from (select *
from (select name, nums from demo)
pivot xml(sum(nums)
for name in(any))) a,
xmltable('/PivotSet' passing a.name_xml columns
芒果 varchar2(30) path 'item[1]/column[2]',
蘋果 varchar2(30) path 'item[2]/column[2]') x
不知是否存在直接進行解析的方法,這種方法還不如直接行列轉變,不通過xml轉來轉去。
select '''' || listagg(substr(name, 1, 30), q'{','}') within group(order by name) || ''''
from (select distinct name from demo);
select *
from (select name, nums from demo)
pivot(sum(nums)
for name in('蘋果', '橘子', '葡萄', '芒果'));
這樣拼接字串反而更加方便。
結論
Pivot 為 SQL 語言增添了一個非常重要且實用的功能。您可以使用 pivot 函式針對任何關係表建立一個交叉表報表,而不必編寫包含大量 decode 函式的令人費解的、不直觀的程式碼。同樣,您可以使用 unpivot 操作轉換任何交叉表報表,以常規關係表的形式對其進行儲存。Pivot 可以生成常規文字或 XML 格式的輸出。如果是 XML 格式的輸出,您不必指定 pivot 操作需要搜尋的值域。