oracle11g - Dynamic sql with pivot/unpivot
问题描述
I want to flip all rows to columns. So if have following 5 columns with 3 data rows
ACCT_ID NAME PHONE MOBILE ALTERNATIVE_NAME
01 JOE BROWN 0456-9992-6666 07767828432 ZOE BROWN
02 GILL SHARP 0456-9992-6666 07763928432 BILL SHARP
03 ZAC LOWE 0236-9992-5644 07663925672 LUKE LOWE
I want the result set to look as follows. So have 3 columns and 5 rows.
COL headings (COL1 - COL3) are only added to make clearer, i don't need columns headings
COL1 COL2 COL3
01 02 03
JOE BROWN GILL SHARP ZAC LOWE
0456-9992-6666 0456-9992-6666 0236-9992-5644
07767828432 07763928432 07663925672
ZOE BROWN BILL SHARP LUKE LOWE
The following SQL will work if know the acct_id's. But i wont know the acct_id, so need to write some dynamic sql to select the acct_id from table REPORTER.TEMP_PSR_REGION and then do the pivot/unpivot.
CREATE TABLE REPORTER.TEMP_PSR_REGION
(
ACCT_ID VARCHAR(50) NOT NULL,
NAME VARCHAR(50) NOT NULL,
PHONE VARCHAR(50) NOT NULL,
MOBILE VARCHAR(50) NOT NULL,
ALTERNATIVE_CONTACT VARCHAR(50) NOT NULL
)
INSERT INTO TEMP_PSR_REGION
(ACCT_ID,
NAME,
PHONE,
MOBILE,
ALTERNATIVE_CONTACT)
VALUES ('01',
'JOE BROWN',
'0456-9992-6666',
'07767828432',
'ZOE BROWN')
INSERT INTO TEMP_PSR_REGION
(ACCT_ID,
NAME,
PHONE,
MOBILE,
ALTERNATIVE_CONTACT)
VALUES ('02',
'GILL SHARP',
'0456-9992-6666',
'07763928432',
'BILL SHARP')
INSERT INTO TEMP_PSR_REGION
(ACCT_ID,
NAME,
PHONE,
MOBILE,
ALTERNATIVE_CONTACT)
VALUES ('03',
'ZAC LOWE',
'0236-9992-5644',
'07663925672',
'LUKE LOWE')
--- PIVOT/UNPIVOT Example
select col1, col2, col3
from (
select t.*, t.acct_id as col_id
from TEMP_PSR_REGION t
)
unpivot
(
value FOR heading in (acct_id, name, phone, mobile, ALTERNATIVE_CONTACT)
)
pivot
(
max(value) for col_id in ('01' as col1, '02' as col2, '03' as col3)
)
order by case heading
when 'ACCT_ID' then 1
when 'NAME' then 2
when 'PHONE' then 3
when 'MOBILE' then 4
when 'ALTERNATIVE_CONTACT' then 5
end
What would the dynamic sql look like to do this. I have not written dynamic sql before?
解决方案
您可以通过查询获得枢轴的IN
子句:
select listagg('''' || acct_id || ''' as col' || rownum, ', ') within group (order by acct_id)
from TEMP_PSR_REGION;
LISTAGG(''''||ACCT_ID||'''ASCOL'||ROWNUM,',')WITHINGROUP(ORDERBYACCT_ID)
------------------------------------------------------------------------
'01' as col1, '02' as col2, '03' as col3
然后,您可以将其用作查询的一部分来生成动态 SQL 语句,然后将该语句作为引用游标打开。在此示例中,我使用 SQL*Plus(或 SQL Developer 或 SQLcl)客户端refcursor
变量来简化如何声明和 sisplay 它:
var rc refcursor
declare
l_sql varchar2(32767);
begin
select
q'[
select *
from (
select t.*, t.acct_id as col_id
from TEMP_PSR_REGION t
)
unpivot
(
value FOR heading in (acct_id, name, phone, mobile, ALTERNATIVE_CONTACT)
)
pivot
(
max(value) for col_id in (]'
|| listagg('''' || acct_id || ''' as col' || rownum, ', ') within group (order by acct_id)
|| q'[)
)
order by case heading
when 'ACCT_ID' then 1
when 'NAME' then 2
when 'PHONE' then 3
when 'MOBILE' then 4
when 'ALTERNATIVE_CONTACT' then 5
end]'
into l_sql
from TEMP_PSR_REGION;
dbms_output.put_line(l_sql); -- for debugging only
open :rc for l_sql;
end;
/
其中大部分是您的原始查询。本质上,它将您的查询放入字符串变量中,但会发生变化
select col1, col2, col3
from (
...
至
q'[
select *
from (
...]'
尽管您可以根据需要与另一个生成该选择列表listagg()
;更重要的是:
...
pivot
(
max(value) for col_id in ('01' as col1, '02' as col2, '03' as col3)
)
...
至:
q'[...
pivot
(
max(value) for col_id in (]'
|| listagg('''' || acct_id || ''' as col' || rownum, ', ') within group (order by acct_id)
|| q'[)
)
...]'
执行该块时,dbms_output
调试会显示生成的语句,这与评估过的原始语句相同listagg()
:
select *
from (
select t.*, t.acct_id as col_id
from TEMP_PSR_REGION t
)
unpivot
(
value FOR heading in (acct_id, name, phone, mobile, ALTERNATIVE_CONTACT)
)
pivot
(
max(value) for col_id in ('01' as col1, '02' as col2, '03' as col3)
)
order by case heading
when 'ACCT_ID' then 1
when 'NAME' then 2
when 'PHONE' then 3
when 'MOBILE' then 4
when 'ALTERNATIVE_CONTACT' then 5
end
PL/SQL procedure successfully completed.
您可以显示打开的参考光标:
print rc
COL1 COL2 COL3
-------------------------------------------------- -------------------------------------------------- --------------------------------------------------
01 02 03
JOE BROWN GILL SHARP ZAC LOWE
0456-9992-6666 0456-9992-6666 0236-9992-5644
07767828432 07763928432 07663925672
ZOE BROWN BILL SHARP LUKE LOWE
使用rownum
有点狡猾,因为标题可能是乱序的(因为没有排序),但似乎你并不关心这些。如果确实很重要,您可以使用子查询为基表中的每一行分配一个数字索引,然后使用该值而不是rownum
.
您可以将它放在一个返回 ref 游标的函数中,但这取决于您将如何调用它并使用结果。var
and只是一个演示,print
可能不是您真正会使用的。
您可能还想考虑让一个报告层(如果有的话)处理数据透视。
推荐阅读
- google-cloud-platform - 限制 gsutil -m 线程
- json - 如何编辑 JSON 结构
- c# - MvcOptions.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes 到底发生了什么变化?
- c# - 正确理解异步/等待。它是如何正确的?
- c# - Xamarin,使用 x:name 找不到另一个按钮
- python - 如何找到列表中第二个元素的最小值
- xslt - 删除与 XSLT 中的数字连接的额外零
- docker - 检测容器内的 docker socket 是属于运行在其中的 docker daemon 还是从容器主机注入的
- javascript - 如何使用 CLI 命令 html2react 将 html 转换为 react 组件?
- python - 是否有警告用户传递的参数将被修改的约定?