【问题标题】:Dynamic sql with pivot/unpivot带数据透视/取消透视的动态 sql
【发布时间】:2018-09-20 01:14:10
【问题描述】:

我想将所有行翻转为列。所以如果有以下 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?

【问题讨论】:

  • 谁能帮忙用Oracle的动态sql回答问题?

标签: oracle11g


【解决方案1】:

您可以通过查询获得枢轴的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

您可以将它放在一个返回引用游标的函数中,但这取决于您将如何调用它并使用结果。 varprint 只是一个演示,可能不是您真正会使用的。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-25
    • 2014-10-16
    • 2010-12-28
    • 2014-07-26
    相关资源
    最近更新 更多