PostgreSQL 實現列轉行問題
阿新 • • 發佈:2021-01-12
1 測試表資料
SELECT relative_label_content FROM frk_s.label_cor_gene relative_label_content ------ AA BB CC
2 列轉行寫法
寫法1:
string_agg
SELECT frwybs,string_agg (relative_label_content,',') as relative_label_content FROM frk_s.label_cor_gene GROUP BY frwybs relative_label_content ------------ AA,BB,CC
寫法2:
array_to_string(ARRAY_AGG (text),')
SELECT frwybs,array_to_string( ARRAY_AGG (DISTINCT relative_label_content),' ) as labels_content FROM frk_s.label_cor_gene GROUP BY frwybs labels_content ------------ AA,CC
補充:PostgreSQL行列轉換(相容oracle pivot unpivot)
oracle11g開始內建了資料透視表pivot table這一功能,可以用來實現行列轉換的功能,但是在資料量較大的時候使用效能就會較差。
pivot語法為:
SELECT ... FROM ... PIVOT [XML] (pivot_clause pivot_for_clause pivot_in_clause ) WHERE ...
oracle pivot使用例子:
–建立測試表並插入資料
create table usr (name varchar2(20),score int,class varchar2(20) ); insert into usr values('a',20,'math'); insert into usr values('a',22,'phy'); insert into usr values('b',23,21,'math'); insert into usr values('c','phy'); insert into usr values('c',24,'math'); insert into usr values('d',25,'phy');
–使用pivot進行行列轉換
SQL> select * from usr 2 pivot( sum(score) for class in ('math','phy') 3 4 5 ); NAME 'math' 'phy' -------------------- ---------- ---------- d 25 23 a 20 22 b 21 23 c 24 22
我們還可以使用unpivot來實現列轉行。
unpivot語法為:
SELECT ... FROM ... UNPIVOT [INCLUDE|EXCLUDE NULLS] (unpivot_clause unpivot_for_clause unpivot_in_clause ) WHERE ...
oracle unpivot使用例子:
–建立測試表並插入資料
CREATE TABLE t1 ( VendorID int,Emp1 int,Emp2 int,Emp3 int,Emp4 int,Emp5 int ); INSERT INTO t1 VALUES (1,4,3,5,4); INSERT INTO t1 VALUES (2,1,5); INSERT INTO t1 VALUES (3,4); INSERT INTO t1 VALUES (4,2,4); INSERT INTO t1 VALUES (5,5);
–使用unpivot進行列轉行
SQL> select * from t1 2 UNPIVOT( orders for Employee in(emp1,emp2,emp3,emp4,emp5) ); 3 4 VENDORID EMPL ORDERS ---------- ---- ---------- 1 EMP1 4 1 EMP2 3 1 EMP3 5 1 EMP4 4 1 EMP5 4 2 EMP1 4 2 EMP2 1 2 EMP3 5 2 EMP4 5 2 EMP5 5 3 EMP1 4 VENDORID EMPL ORDERS ---------- ---- ---------- 3 EMP2 3 3 EMP3 5 3 EMP4 4 3 EMP5 4 4 EMP1 4 4 EMP2 2 4 EMP3 5 4 EMP4 5 4 EMP5 4 5 EMP1 5 5 EMP2 1 VENDORID EMPL ORDERS ---------- ---- ---------- 5 EMP3 5 5 EMP4 5 5 EMP5 5 25 rows selected.
那麼在pg中該如何實現oracle的pivot/unpivot的行列轉行功能呢?pg中自帶的tablefunc外掛可以實現,我們可以使用該外掛中的crosstab函式介面進行行列轉換。
pg行轉列例子:
–建表插入測試資料
create table tbl (seller text,se_year int,se_month int,se_amount int); insert into tbl values ('test1',2020,01,123456); insert into tbl values ('test1',02,234567); insert into tbl values ('test1',03,345678); insert into tbl values ('test1',04,05,567890); insert into tbl values ('test2',12); insert into tbl values ('test2',23); insert into tbl values ('test2',34); insert into tbl values ('test2',45); insert into tbl values ('test2',56); insert into tbl values ('test3',12); insert into tbl values ('test3',45); insert into tbl values ('test3',56); insert into tbl values ('test4',20); insert into tbl values ('test4',30); insert into tbl values ('test4',40); insert into tbl values ('test4',50); insert into tbl values ('test1',2019,567890); insert into tbl values ('test1',06,07,08,09,10,11,12,234567); insert into tbl values ('test2',23); insert into tbl select * from tbl;
–行轉列
bill=# select bill-# js->>'seller' as seller,bill-# js->>'se_year' as se_year,bill-# jan,bill-# feb,bill-# mar,bill-# apr,bill-# may,bill-# jun,bill-# jul,bill-# aug,bill-# sep,bill-# oct,bill-# nov,bill-# dec bill-# from crosstab( bill(# -- 這個是需要進行行列變換的源SQL , 資料來源。 bill(# -- 排序欄位為group by欄位 ,最後一個欄位為轉換後的內容欄位,導數第二個欄位為行列變換的欄位(內容為列舉,比如月份) bill(# -- (必須在下一個引數中提取出對應的所有列舉值) bill(# $$select jsonb_build_object('seller',seller,'se_year',se_year) as js,se_month,sum(se_amount) from tbl group by 1,2 order by 1$$,bill(# -- 行列轉換的行,有哪些值被提取出來作為列。 這個在這裡代表的是月份,也就是se_month的值 bill(# -- 或(select * from (values('jan'),...('dec')) t(se_month)) bill(# 'select distinct se_month from tbl order by 1' bill(# ) bill-# as -- crosstab 輸出格式 bill-# ( js jsonb,-- 第一個引數SQL內對應的order by對應的欄位(1個或多個) bill(# Jan numeric,-- 第一個引數SQL內對應導數第二個欄位的列舉值,(行轉列) bill(# feb numeric,-- ...同上 bill(# mar numeric,bill(# apr numeric,bill(# may numeric,bill(# jun numeric,bill(# jul numeric,bill(# aug numeric,bill(# sep numeric,bill(# oct numeric,bill(# nov numeric,bill(# dec numeric bill(# ) bill-# order by 1,2; seller | se_year | jan | feb | mar | apr | may | jun | jul | aug | sep | oct | nov | dec --------+---------+--------+--------+--------+--------+---------+--------+--------+--------+--------+---------+--------+-------- test1 | 2019 | 246912 | 469134 | 691356 | 691356 | 1135780 | 246912 | 469134 | 691356 | 691356 | 1135780 | 246912 | 469134 test1 | 2020 | 246912 | 469134 | 691356 | 691356 | 1135780 | | | | | | | test2 | 2019 | | | | | | | | | | | 24 | 46 test2 | 2020 | 24 | 46 | 68 | 90 | 112 | | | | | | | test3 | 2020 | | | 24 | 90 | 112 | | | | | | | test4 | 2020 | | 40 | 60 | 80 | 100 | | | | | | | (6 rows)
–列轉行
bill=# with a as ( -- A對應原始資料(即需要列轉行的資料) bill(# select bill(# js->>'seller' as seller,bill(# js->>'se_year' as se_year,bill(# jan,bill(# feb,bill(# mar,bill(# apr,bill(# may,bill(# jun,bill(# jul,bill(# aug,bill(# sep,bill(# oct,bill(# nov,bill(# dec bill(# from crosstab( bill(# -- 這個是需要進行行列變換的源SQL , 資料來源。 bill(# -- 排序欄位為group by欄位 ,最後一個欄位為轉換後的內容欄位,導數第二個欄位為行列變換的欄位(內容為列舉,比如月份) bill(# -- (必須在下一個引數中提取出對應的所有列舉值) bill(# $$select jsonb_build_object('seller',...('dec')) t(se_month)) bill(# 'select distinct se_month from tbl order by 1' bill(# ) bill(# as -- crosstab 輸出格式 bill(# ( js jsonb,bill(# dec numeric bill(# ) bill(# order by 1,2 bill(# ) bill-#,bill-# -- b , 用jsonb把多列合併為一列,並使用jsonb_each展開。 bill-# b as (select seller,se_year,jsonb_each(row_to_json(a)::jsonb-'seller'::text-'se_year'::text) as rec from a) bill-# select seller,(b.rec).key as month,(b.rec).value as sum from b; seller | se_year | month | sum --------+---------+-------+--------- test1 | 2019 | apr | 691356 test1 | 2019 | aug | 691356 test1 | 2019 | dec | 469134 test1 | 2019 | feb | 469134 test1 | 2019 | jan | 246912 test1 | 2019 | jul | 469134 test1 | 2019 | jun | 246912 test1 | 2019 | mar | 691356 test1 | 2019 | may | 1135780 test1 | 2019 | nov | 246912 test1 | 2019 | oct | 1135780 test1 | 2019 | sep | 691356 test1 | 2020 | apr | 691356 test1 | 2020 | aug | null test1 | 2020 | dec | null test1 | 2020 | feb | 469134 test1 | 2020 | jan | 246912 test1 | 2020 | jul | null test1 | 2020 | jun | null test1 | 2020 | mar | 691356 test1 | 2020 | may | 1135780 test1 | 2020 | nov | null test1 | 2020 | oct | null test1 | 2020 | sep | null test2 | 2019 | apr | null test2 | 2019 | aug | null test2 | 2019 | dec | 46 test2 | 2019 | feb | null test2 | 2019 | jan | null test2 | 2019 | jul | null test2 | 2019 | jun | null test2 | 2019 | mar | null test2 | 2019 | may | null test2 | 2019 | nov | 24 test2 | 2019 | oct | null test2 | 2019 | sep | null test2 | 2020 | apr | 90 test2 | 2020 | aug | null test2 | 2020 | dec | null test2 | 2020 | feb | 46 test2 | 2020 | jan | 24 test2 | 2020 | jul | null test2 | 2020 | jun | null test2 | 2020 | mar | 68 test2 | 2020 | may | 112 test2 | 2020 | nov | null test2 | 2020 | oct | null test2 | 2020 | sep | null test3 | 2020 | apr | 90 test3 | 2020 | aug | null test3 | 2020 | dec | null test3 | 2020 | feb | null test3 | 2020 | jan | null test3 | 2020 | jul | null test3 | 2020 | jun | null test3 | 2020 | mar | 24 test3 | 2020 | may | 112 test3 | 2020 | nov | null test3 | 2020 | oct | null test3 | 2020 | sep | null test4 | 2020 | apr | 80 test4 | 2020 | aug | null test4 | 2020 | dec | null test4 | 2020 | feb | 40 test4 | 2020 | jan | null test4 | 2020 | jul | null test4 | 2020 | jun | null test4 | 2020 | mar | 60 test4 | 2020 | may | 100 test4 | 2020 | nov | null test4 | 2020 | oct | null test4 | 2020 | sep | null (72 rows)
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援我們。如有錯誤或未考慮完全的地方,望不吝賜教。