日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

oracle 动态sql列转行_Oracle 行转列 动态出转换的列

發布時間:2025/3/21 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 oracle 动态sql列转行_Oracle 行转列 动态出转换的列 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

10月的第二天,前天寫了個Oracle中行轉列的pivot的基本使用方法,然后,因為pivot的用法中,正常情況下,我們需要轉出多少個列,都得在我們的sql中完完整整地寫出,而不能直接在里面寫個查詢來動態轉換。然后,趁著祖國母親的生日,這幾天放假,整理一下處理方法。

一、運行環境

Win10,Oracle Database 11g r2,plsql 12。

二、效果預覽

1、固定轉換列的方法

2、存儲過程處理

1)調用存儲過程

2)查指定的視圖即可

3、兩種方法的關系

其實原理很簡單,就是通過動態sql,去把你不愿意寫,或者說是不確定的轉換列數,通過查詢查出來,拼接進去,然后執行拼接后的sql,創建視圖。

三、存儲過程

create or replace procedure p_RowsToCols(as_sql in varchar2 --源數據的查詢sql

,as_sql_cols in varchar2 --動態轉換列的查詢sql,要求轉為列的那列,字段名必須為cols,支持排序

,as_aggCol in varchar2 --對應pivot函數的 聚合函數

,as_changeCol in varchar2 --源數據中,要轉為列的字段名

,as_viewName in varchar2 --結果輸出的視圖名,執行完后查此視圖即可

) is

ls_sql varchar2(4000);

ls_in varchar2(4000);

begin

--拼接in的內容

ls_sql := 'select listagg(''''''''||cols||'''''' "''||cols||''"'', '','')within group(order by rn) ' ||

'from (select rownum rn, cols from (' || as_sql_cols || '))';

execute immediate ls_sql

into ls_in;

--創建視圖

ls_sql := 'create or replace view ' || as_viewName ||' as ' ||

'select * from (' || as_sql || ') ' ||

'pivot (' || as_aggCol || ' for ' || as_changeCol || ' in (' || ls_in || '))';

execute immediate ls_sql;

end p_RowsToCols;

四、測試數據及SQL

貼一下我測試的數據和代碼。

--建表

--drop table SalesList;

create table SalesList(

keHu varchar2(20), --客戶

shangPinId number(8), --商品Id

shangPin varchar2(20), --商品名稱

salesNum number(8) --銷售數量

);

--插入數據

declare

--談幾個客戶

cursor lr_kh is

select regexp_substr('張三、李四、王五、趙六','[^、]+',1, level) keHu from dual

connect by level <= 4;

--進點貨

cursor lr_sp is

select level shangPinId, regexp_substr('上衣、褲子、襪子、帽子','[^、]+',1, level) shangPin from dual

connect by level <= 4;

begin

--循環插入

for v_kh in lr_kh loop

for v_sp in lr_sp loop

insert into SalesList

select v_kh.keHu, v_sp.shangPinId, v_sp.shangPin, floor(dbms_random.value(10,50)) from dual;

end loop;

end loop;

commit;

end;

/

--看下源數據

select * from salesList a;

--固定行轉列

select *

from (select kehu, shangPin, salesNum from salesList) pivot(

max(salesNum) for shangPin in (

'上衣' as 上衣,

'褲子' as 褲子,

'襪子' as 襪子,

'帽子' as 帽子

)

);

--動態行轉列

call p_RowsToCols('select keHu, shangPin, salesNum from salesList',

'select distinct shangPinId, shangPin cols from salesList order by shangPinId',

'max(salesNum)',

'shangPin',

'sales_RowsToCols');

select * from sales_RowsToCols;

完結!!!!!!!!!!!!!!!!!!!!!!

結尾來個悲傷的彩蛋,聞者傷心,聽者落淚!

上面介紹的方法是一個很簡單的思路,然鵝!!!在找到這個思路之前,我還傻傻的做了另外一個版本,一個比較復雜的版本。。。花了有差不多半天時間。我按照這個思路,剛做完的時候有多開心,現在的我,就有多傷心!!不過不忍心直接del掉,還是在這邊記錄一下吧,思路大概是這個樣幾:

同樣是存儲過程,傳入數據源的查詢sql,通過存儲過程處理,拼接成完整的pivot函數需要的sql,然后直接執行,查出結果,insert到一張通用的記錄表中,然后建一個視圖,指向這部分數據。

完結篇.1?效果預覽

測試數據參考上面的插入sql,同樣的數據。

1、第一個參數:必填,數據源的查詢,列數不限,但查詢的倒數第二列為轉換列,最后一列數據列;

2、第二個參數:

1)可為空,內容為要轉換出的列的查詢sql,可指定表頭順序及顯示的表頭名稱。為空時即從數據源查詢的倒數第二列中取distinct值,并不保證排序。

2)查詢結果必須為三列,第一列為數字排序列(不需要的話這列就隨便指定一個數字就行),第二列對應數據源中轉換列的值,第三列即為對應的表頭轉換后的名稱(如果名稱不需要改變,這列跟第二列保持一樣就行)。

3、第三個參數:可為空,內容就是處理完后要查的視圖名。為空的話,即默認為tmp_rowToCol。

完結篇.2?跟pivot的對應關系

完結篇.3?其他好玩的東西

1、因為我們這個方法的話是吧查出的內容存到表里,然后通過視圖直接指向數據的,剛才指定的視圖Sale_RowToCol,具體創建語法,就是下面這個,tmp_RowToCol_XiaoXianNv為數據存儲的表,fbs = '1'?表示是轉換的實際數據,fguid是本次轉換的一個key。

我們查下這個表中實際的數據,這是一個在首次調用存儲過程的時候會創建的一個表,203個字段,不超過這么多列的都可以通過我們的存儲過程去轉并存儲數據。

然后,這個表的數據是會一直保存著的,emm...好像沒啥卵用,不過說不定哪天腦子抽了向往前查查這個還是挺有意思的。。。(說得我自己都不信)

完結篇.4?貼代碼

csdn的高亮。。好像不太友好

/**

* 動態進行行列轉換,結果集可在一個可指定的視圖中查詢(默認為tmp_rowToCol)

* 適用于把一列的值轉成多列,轉換效果與Oracle的pivot相同,但不需要寫死轉換出來的每一個列

* 轉換后的數據所存的實體表為tmp_RowToCol_XiaoXianNv,通過一個guid關聯到指定的視圖

* tmp_RowToCol_XiaoXianNv表在此過程中不做刪除操作。所以如果永久了怕是數據也會挺多。

* 如果不需要保留數據的話,可以考慮把這個表建為一個會話級臨時表,然后轉換結果插入后不提交。

* 這樣在同一會話下可查詢,提交或者回滾后數據就不復存在。

*

* 轉換思路:

* 1、通過動態sql,拼接出 for XXX in () 里面那部分內容,然后通過動態sql執行并把結果插入一個表中

* 2、獲取固定列、轉出列的列名,進行拼接,然后創建視圖指向上一步插入的數據

*

* author: lhy

* date: 2018-10-01 祖國萬歲

*

* as_sql 要轉換的數據源查詢

* 對查詢結果集的要求:至少3列,

* 最后一列為數據值

* 倒數第二列為要轉成列的內容

* 前面的就是不需轉換的列

* as_sql_col 查詢要轉的列名,如果不指定,即從as_sql的查詢的倒數第二列中獲取distinct值

* 對查詢的結果集要求:必須為三列

* 對應pivot函數中的:for xxx in('值1' as colNm1,'值2' as colNm2 ...)

* 第一列:排序列,要求為數字

* 第二列:值(值1..值2)

* 第三列:字段名(colNm1..colNm2)

* 當然,你不care最后結果的字段的排序和字段名的話,第一列您直接指定一個固定值就行,第三列跟第二列一樣也行

* as_tableName 指定一個視圖名來存放轉換后的數據,調用存儲過程后,通過此視圖查詢結果集

*/

create or replace procedure p_rowToCol(as_sql in varchar2, as_sql_col in varchar2, as_viewName in varchar2) is

lr_curid integer; --游標id

ls_cnt number(8); --計數用

ls_sql varchar2(4000); --sql語句

ls_sql_col varchar2(4000); --同 as_sql_col

ls_rsltTab dbms_sql.desc_tab; --存放返回的結果集

ls_viewName varchar2(200); --轉換結果存放的表名

ls_guid varchar2(50); --當次轉換的guid

ls_aggColNm varchar2(50); --對應pivot的聚合列的列名

ls_changeColNm varchar2(50); --轉換列的列名

ls_cnt_col number(8); --要轉換出來的列數

ls_in_text varchar2(4000); --對應for()的內容

ls_cnt_end number(8); --最終查詢結果的列數

ls_sql_end varchar2(4000); --最終的插入語句

ls_col_add varchar2(4000); --存放轉出的列名

ls_col_fixed varchar2(4000); --存放不需要轉換的列名

ls_col_insert varchar2(4000); --存放插入的字段

ls_col_view varchar2(4000); --視圖的字段

ls_sql_view varchar2(4000); --存放最后的視圖的sql

ls_thead varchar2(4000); --拼接一個表頭出來,說不定可以回查

begin

--兩步準備工作,其實如果做過一次,后面的代碼中其實都不需要執行這兩步了

--準備工作1、看下是否存在tmp_RowToCol_XiaoXianNv這個表,首次使用不存在的話建一個(用于存放轉換后的數據)

select count(*) into ls_cnt from all_tables where table_name = upper('tmp_RowToCol_XiaoXianNv');

if ls_cnt = 0 then

ls_sql := 'create table tmp_RowToCol_XiaoXianNv(fguid varchar2(50),fopdt date default sysdate,fbs varchar2(8),';

for i in 1..200 loop

if i = 200 then

ls_sql := ls_sql || 'C' || i || ' varchar2(4000))';

else

ls_sql := ls_sql || 'C' || i || ' varchar2(4000),';

end if;

end loop;

execute immediate ls_sql;

--怕以后數據多查詢慢的話還可以建個索引給fguid字段

execute immediate 'create index IDX_ROWSTOCOLS_FGUID on tmp_RowToCol_XiaoXianNv (fguid)';

end if;

--準備工作2、看下是否存在一個tmp_XiaoXianNv_t1這個臨時表,首次使用不存在的話建一個(用于處理轉換列排序)

select count(*) into ls_cnt from all_tables where table_name = upper('tmp_XiaoXianNv_t1');

if ls_cnt = 0 then

ls_sql := 'create global temporary table tmp_XiaoXianNv_t1(fseq NUMBER(20),c1 VARCHAR2(4000),c2 VARCHAR2(4000)) on commit delete rows';

execute immediate ls_sql;

end if;

--取個guid,準備開干

--這個就是這一次轉換的的key,以后要找這次轉換的數據都可以拿著這個key到tmp_RowToCol_XiaoXianNv找

--所以其實也可以通過傳參來手動指定這個key,然后以后想查回來這次的數據都會比較方便

ls_guid := sys_guid();

--獲取轉換列和聚合列的列名,即as_sql查詢結果的倒數兩列

ls_sql := as_sql;

lr_curid := dbms_sql.open_cursor;

dbms_sql.parse(lr_curid, ls_sql, dbms_sql.native);

dbms_sql.describe_columns(lr_curid, ls_cnt, ls_rsltTab);

ls_changeColNm := ls_rsltTab(ls_cnt - 1).col_name; --倒數第2列,獲取轉換列列名

ls_aggColNm := ls_rsltTab(ls_cnt).col_name; --倒數第1列,獲取聚合列列名

ls_cnt := ls_cnt - 2; --不需要轉換的列數

--拼接不需要轉換的列名,用于后面建視圖(part 1)

for i in 1..ls_cnt loop

ls_col_fixed := ls_col_fixed || ls_rsltTab(i).col_name || ', ';

end loop;

dbms_sql.close_cursor(lr_curid);

--拼接 for xxx in ('值1' as colNm1,'值2' as colNm2 ...) 部分

--獲取所有列名并拼接

--1、先把所有列名的查詢sql搞定

if as_sql_col is null then

ls_sql_col := 'select rownum rn, c1, c1 c2 from (select distinct '|| ls_changeColNm || ' c1 from (' || as_sql || ') order by ' || ls_changeColNm || ')';

else

ls_sql_col := as_sql_col;

end if;

--2、把轉換列的數據插入到臨時表

execute immediate 'delete from tmp_XiaoXianNv_t1';

ls_sql := 'insert into tmp_XiaoXianNv_t1 (fseq, c1, c2) '|| ls_sql_col;

execute immediate ls_sql;

--3、ls_cnt_col count出要轉換出的列數

execute immediate 'select count(*) from tmp_XiaoXianNv_t1' into ls_cnt_col;

--順便算一下最終查詢結果的列數

ls_cnt_end := ls_cnt + ls_cnt_col;

--4、拼接for xx in () 里面的內容

ls_sql := 'select listagg(''''''''||c1||'''''' ''||c2 , '', '') within group(order by fseq ) from tmp_XiaoXianNv_t1 a';

execute immediate ls_sql into ls_in_text;

--5、順便拼接出行轉列轉換出來的字段名,用于后面建視圖(part 2)

ls_sql := 'select listagg(c2,'','')within group(order by fseq) from tmp_XiaoXianNv_t1';

execute immediate ls_sql into ls_col_add;

--拼接插入的表的字段 tmp_RowToCol_XiaoXianNv(c1,c2,c3...)

select listagg(col, ', ') within group(order by rn)

into ls_col_insert

from (select rownum rn, 'c' || rownum col

from dual

connect by rownum <= ls_cnt_end);

--拼接pivot的insert sql,插入內容,fbs為標識字段,標記為1,即為正式數據

ls_sql_end := 'insert into tmp_RowToCol_XiaoXianNv (fguid,fbs,' || ls_col_insert || ') '

||'select '''|| ls_guid ||''' fguid,''1'',t.* from ('

|| as_sql || ') PIVOT(max(' || ls_aggColNm || ') for ' || ls_changeColNm || ' in ('

|| ls_in_text || ')) t' ;

execute immediate ls_sql_end;

commit;

--拼接表頭的字段

ls_thead := ls_col_fixed || ls_col_add;

ls_thead := replace(ls_thead,' ');

ls_col_view := ls_thead; --轉存一下給下面拼接視圖的使用

select listagg(''''||col||'''',',')within group(order by rn)

into ls_thead from (

select level rn, regexp_substr(ls_thead,'[^,]+',1,level) col

from dual connect by level <= ls_cnt_end

);

--拼接pivot的insert sql,插入內容,fbs為標識字段,標記為轉換后的字段數,即ls_cnt_end變量,即為正式數據

ls_sql := 'insert into tmp_RowToCol_XiaoXianNv (fguid,fbs,' || ls_col_insert || ') values('''|| ls_guid ||''','''||ls_cnt_end||''','||ls_thead||')';

execute immediate ls_sql;

commit;

--拿到結果視圖名,默認為tmp_rowToCol

if as_viewName is null then

ls_viewName := 'tmp_rowToCol';

else

ls_viewName := as_viewName;

end if;

/***************************************這部分的代碼可以直接刪掉************************************

--上面是根據前面的數據拼接出來的視圖的字段ls_col_view,如果我們是只知道一個guid的時候,我們其實也可以去從數據表中查出表頭,然后拼接

ls_sql := 'select max(fbs) from tmp_RowToCol_XiaoXianNv where fguid = '''||ls_guid||''' and fbs <> ''1''';

execute immediate ls_sql into ls_cnt_end; --獲取列數

select listagg(col,'||'',''||')within group(order by rn)

into ls_sql from (

select level rn, 'C'||level col from dual connect by level <= ls_cnt_end

);

ls_sql := 'select '|| ls_sql || ' from tmp_RowToCol_XiaoXianNv where fguid = '''||ls_guid||''' and fbs <> ''1''';

execute immediate ls_sql into ls_col_view; --獲取視圖列名ls_col_view,這里得到的跟上面獲取到的是一樣的

**********************************************************************************************/

--拼接視圖的字段別名轉換關系 c1 字段1, c2 字段2 ...

select listagg(c1||' '||col,',')within group(order by rn)

into ls_col_view from (

select 'C'||level c1, level rn, regexp_substr(ls_col_view,'[^,]+',1,level) col

from dual connect by level <= ls_cnt_end

);

--視圖呈現

ls_sql_view := 'create or replace view '|| ls_viewName ||' as select '|| ls_col_view || ' from tmp_RowToCol_XiaoXianNv where fbs = ''1'' and fguid = '''|| ls_guid || '''';

execute immediate ls_sql_view;

end p_rowToCol;

完結篇.5?測試SQL

--建表

--drop table SalesList;

create table SalesList(

keHu varchar2(20), --客戶

shangPinId number(8), --商品Id

shangPin varchar2(20), --商品名稱

salesNum number(8) --銷售數量

);

--插入數據

declare

--談幾個客戶

cursor lr_kh is

select regexp_substr('張三、李四、王五、趙六','[^、]+',1, level) keHu from dual

connect by level <= 4;

--進點貨

cursor lr_sp is

select level shangPinId, regexp_substr('上衣、褲子、襪子、帽子','[^、]+',1, level) shangPin from dual

connect by level <= 4;

begin

--循環插入

for v_kh in lr_kh loop

for v_sp in lr_sp loop

insert into SalesList

select v_kh.keHu, v_sp.shangPinId, v_sp.shangPin, floor(dbms_random.value(10,50)) from dual;

end loop;

end loop;

commit;

end;

/

--查看下數據

select * from salesList a;

--固定行轉列

select *

from (select kehu, shangPin, salesNum from salesList) pivot(

max(salesNum) for shangPin in (

'上衣' as 上衣數量,

'褲子' as 褲子數量,

'襪子' as 襪子數量,

'帽子' as 帽子數量

)

);

--動態行轉列

call p_rowtocol('select keHu, shangPin, salesNum from SalesList',

'',

'Sale_RowToCol');

select * from Sale_RowToCol;

--完整版

call p_rowtocol('select keHu 客戶, shangPin, salesNum from SalesList',

'select distinct shangPinId, shangPin, shangPin||''數量'' from salesList order by shangPinId',

'Sale_RowToCol');

select * from Sale_RowToCol;

--數據存儲的表

select * from tmp_RowToCol_XiaoXianNv

結束語?沒有

啊對,這個存儲過程中有建表和建視圖的語法,如果你的用戶沒有權限的話需要用dba用戶給一下權限:

grant create table to user;

grant create view to user;

================2019年4月25日 更新================

評論區一個小伙伴報的bug。

拼接出來的sql語句,pivot(xxx for xxx in ('0' 0, '1' 1, '2' 2)) 的這部分,當列為純數字的時候,別名要加個雙引號。

就是說,拼接出來的應該是 pivot(xxx for xxx in ('0' "0", '1' "1", '2' "2")) 才對。

修改內容:代碼的11行,拼接列別名的時候,添加個雙引號把列名包住

更新后代碼如下:

嗯,再次感謝提bug的小伙伴。

————————————————

版權聲明:本文為CSDN博主「huay_li」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/huay_li/article/details/82924443

總結

以上是生活随笔為你收集整理的oracle 动态sql列转行_Oracle 行转列 动态出转换的列的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。