使用pivot/unpivot/wmconcat/listagg等進行‘行列轉換’之詳細解析
閒話少說,直入主題!
此文是基於其他部落格部分框架以及大致內容進行了個人層面的詳細分析和改進。
1. wmconcat
同一列的列值,根據相應分組規則 ,將各自組內的值,存於同一個單元格里,存於clob欄位中,以','間隔;
如果直接使用wmconcat而沒有分組,則是將同一個列的值,轉換後都存於同一個單元格里,預設是以逗號','間隔,可以改變。
(1)建立測試表並插入資料
create table test_wm_concat(id number,name varchar2(20)); insert into test_wm_concat values(1,'a'); insert into test_wm_concat values(1,'b'); insert into test_wm_concat values(1,'c'); insert into test_wm_concat values(2,'d'); insert into test_wm_concat values(2,'e');
(2)查詢測試表test_wm_concat的資料
select * from test_wm_concat;
(3)使用wm_concat查詢資料(直接查詢以及分組後查詢)
select wm_concat(name) name from test_wm_concat;
可以通過to_char將clob轉成varchar2的形式
select to_char(wm_concat(name)) name from test_wm_concat;
按照id分組,合併name,同組的存於一個clob欄位中
select id, wm_concat(name) name from test_wm_concat group by id;
(4)使用wm_concat查詢資料,存於clob欄位中,以'|'間隔,而預設是','
select replace(wm_concat(name), ',', '|') name from test_wm_concat;
(5)使用wm_concat和decode組合將列按照列值,拆分成n個列展示(笨方法);
想根據哪個欄位展示哪個值,在decode裡自由選擇即可;
將clob欄位to_char轉成字元型;
SELECT to_char(WM_CONCAT(a)) a,
to_char(WM_CONCAT(b)) b,
to_char(WM_CONCAT(c)) c,
to_char(WM_CONCAT(d)) d,
to_char(WM_CONCAT(e)) e
FROM (SELECT DECODE(name, 'a', id, NULL) a,
DECODE(name, 'b', id, NULL) b,
DECODE(name, 'c', id, NULL) c,
DECODE(name, 'd', id, NULL) d,
DECODE(name, 'e', id, NULL) e
FROM test_wm_concat);
(6)對wm_concat轉換後的單元格進行解析,將以逗號區分的n個值,拆分回n行展示;
也就是解析clob欄位,將存於同一個clob欄位的多值,解析成多行資料(以回車為間隔)。
select * from table(splitstr_rows((select replace(wm_concat(name),',',chr(10)) from test_wm_concat), chr(10)));
(7)wmconcat實際應用案例
比如根據基表建立時檢視,基表有多個欄位,類似"create or replace view as select 欄位1,...欄位n from tablename",
使用wm_concat可以快速取出這些欄位。
假設我的emp表中有(employee_id .... department_id)11個欄位。查詢結果如下:
select column_name
from user_tab_columns
where table_name = 'EMP';
select to_char('create or replace view as select ' ||
wm_concat(column_name) || ' from emp;') sqlStr
from user_tab_columns
where table_name = 'EMP';
2.listagg
listagg與wmconcat效果類似,都是將列值存於同一個單元格里,但是listagg更靈活,可以進行排序等操作。
(1)排序操作:直接使用wmconcat和 listagg within group 進行比對
select to_char(wm_concat(employee_id)) from emp;
select (listagg(employee_id, ',') within group(order by employee_id)) employee_id
from emp;
(2)分組後排序操作:使用分組統計各個部門的所有員工列表。
select department_id,
(listagg(employee_id, ' ,') within group(order by employee_id)) employee_id
from emp
group by department_id;
(3)還可以與分析函式等組合操作
3.
pivot,行轉列(把一列根據列值拆分成多列)
pivot(聚合函式 for 列名 in(型別)),其中 in('') 中可以指定別名;
(1)建立測試表並插入資料
測試資料 (id,型別名稱,銷售數量)
create table demo(id int,name varchar(20),nums int);
insert into demo values(1, '蘋果', 1000);
insert into demo values(2, '蘋果', 2000);
insert into demo values(3, '蘋果', 4000);
insert into demo values(4, '橘子', 5000);
insert into demo values(5, '橘子', 3000);
insert into demo values(6, '葡萄', 3500);
insert into demo values(7, '芒果', 4200);
insert into demo values(8, '芒果', 5500);
(2)查詢測試表資料
select * from demo;
(3)案例:根據水果的型別為列標題查詢出一條資料顯示出每種型別的銷售數量。
笨方法1:使用這種方式,一定要構造出from 後的是 “新列名” 與 “展示值” 是 一對一 的關係
SELECT to_char(wm_concat(DECODE(name, '蘋果', nums, NULL))) 蘋果,
to_char(wm_concat(DECODE(name, '橘子', nums, NULL))) 橘子,
to_char(wm_concat(DECODE(name, '葡萄', nums, NULL))) 葡萄,
to_char(wm_concat(DECODE(name, '芒果', nums, NULL))) 芒果
FROM (select name, sum(nums) nums from demo group by name);
笨方法2:多項子查詢
select *
from (select sum(nums) 蘋果 from demo where name = '蘋果'),
(select sum(nums) 橘子 from demo where name = '橘子'),
(select sum(nums) 葡萄 from demo where name = '葡萄'),
(select sum(nums) 芒果 from demo where name = '芒果');
笨方法3:利用decode 函式
select sum(decode(name, '蘋果', nums)) 蘋果,
sum(decode(name, '橘子', nums)) 橘子,
sum(decode(name, '葡萄', nums)) 葡萄,
sum(decode(name, '芒果', nums)) 芒果
from demo;
簡便方法:使用pivot (把一列根據列值拆分成多個列值列)
select *
from (select name, nums from demo) pivot(sum(nums) for name in('蘋果' 蘋果,
'橘子' 橘子,
'葡萄' 葡萄,
'芒果' 芒果));
4.
unpivot,列轉行 (把多列合成一列按行展示)
(1)建立測試表並插入資料
create table Fruit(id int,name varchar(20), Q1 int, Q2 int, Q3 int, Q4 int);
insert into Fruit values(1,'蘋果',1000,2000,3300,5000);
insert into Fruit values(2,'橘子',3000,3000,3200,1500);
insert into Fruit values(3,'香蕉',2500,3500,2200,2500);
insert into Fruit values(4,'葡萄',1500,2500,1200,3500);
(2)查詢測試表資料
select * from Fruit;
(3)案例:水果表裡記錄了4個季度的銷售數量,現在要將每種水果的每個季度的銷售情況用多行資料展示。
select id, name, jidu, xiaoshou
from Fruit unpivot(xiaoshou for jidu in(q1, q2, q3, q4));
效果相同的笨方法:
select id, name, 'Q1' jidu, (select q1 from fruit where id = f.id) xiaoshou
from Fruit f
union
select id, name, 'Q2' jidu, (select q2 from fruit where id = f.id) xiaoshou
from Fruit f
union
select id, name, 'Q3' jidu, (select q3 from fruit where id = f.id) xiaoshou
from Fruit f
union
select id, name, 'Q4' jidu, (select q4 from fruit where id = f.id) xiaoshou
from Fruit f;
5.
OVER。