具有枢轴/不枢轴的动态SQL

时间:2018-08-01 09:08:43

标签: oracle11g

我想将所有行翻转为列。所以如果有以下5列 有3个数据行

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

我希望结果集如下所示。所以有3列5行。

仅添加了COL标题(COL1-COL3)以使内容更清晰,我不需要 列标题

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

如果知道acct_id,以下SQL将起作用。但是我不知道acct_id,因此需要编写一些动态sql从表REPORTER.TEMP_PSR_REGION中选择acct_id,然后执行数据透视/取消数据透视。

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

动态sql会执行以下操作。我以前没有写过动态sql吗?

1 个答案:

答案 0 :(得分:0)

您可以通过查询获取枢轴的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语句,然后将该语句作为ref游标打开。在此示例中,我使用一个SQL * Plus(或SQL Developer或SQLcl)客户端refcursor变量来简化如何声明和播放它:

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游标的函数中,但这取决于您如何调用它并使用结果。 varprint只是一个演示,可能不是您真正使用的。

您可能还需要考虑让报告层(如果有的话)处理数据透视。

相关问题