【问题标题】:dynamic pivot SQL Query in DB2DB2 中的动态数据透视 SQL 查询
【发布时间】:2021-08-26 17:05:03
【问题描述】:

我在 DB2 中有如下表,其中所有这 3 列都是 VARCHAR 数据类型

DATE_F    FILE_NM   ROW_ID  COLUMN_NM   COLUMN_VAL 
20200131  ABC.TXT     1      ABC_COL1     123
20200131  ABC.TXT     1      ABC_COL2     XYZ
20201231  ABC.TXT     2      ABC_COL1     456
20201231  ABC.TXT     2      ABC_COL2     XY1
20200630  XYZ.TXT     1      XYZ_COL1     PQR
20200630  XYZ.TXT     1      XYZ_COL2     567
20200630  XYZ.TXT     1      XYZ_COL3     MNO

我需要一个动态 PIVOT 查询,当 FILE_NM 过滤器用作 ABC.TXT 时,它应该以行格式选择 ABC_COL1 和 ABC_COL2 的值

当 FILE_NM 过滤器用作 XYZ.TXT 时,相同的查询应该以行格式选择 XYZ_COL1、XYZ_COL2 和 XYZ_COL3 的值

当 FILE_NM=ABC.TXT 时输出应如下所示

DATE_F    ROW_ID   ABC_COL1   ABC_COL2    
20200131    1        123       XYZ
20201231    2        456       XY1

当 FILE_NM=XYZ.TXT 时输出应如下所示

 DATE_F    ROW_ID     XYZ_COL1     XYZ_COL2     XYZ_COL3
20200630    1           PQR         567           MNO

因此,一个查询将能够提取数据。在这种情况下,任何功能或等效功能都可以完成任务吗?

【问题讨论】:

    标签: sql db2 pivot dynamic-pivot


    【解决方案1】:

    您可以尝试以下通用存储过程进行旋转。

    --#SET TERMINATOR @
    create or replace procedure pivot
    (
      in  sel_stmt varchar(4000)
    , in  row_cols varchar(200)
    , in  col_col  varchar(128)
    , in  agg_col  varchar(128)
    , in  agg_fn   varchar(10)
    , in  tmp_tbl  varchar(128)
    , in  null_ind varchar(10)
    , out rc       int
    , out msg      varchar(128)
    , out stmt     varchar(4000)
    )
    LANGUAGE SQL
    DYNAMIC RESULT sets 1
    BEGIN
     declare QUOT1        char(1) default '''';
     declare QUOT2        char(1) default '"';
     declare SQLCODE      int default 0;
     declare SQLTYPE_ID   int;
     declare SQLTYPE      varchar(128);
     declare SQLLENGTH    int;
     declare SQLSCALE     int;
     declare SQLNAME_DATA varchar(128);
     declare SQLTYPEF     varchar(128);
     declare col_val      varchar(4000);
     declare apo          varchar(1);
     declare l1 RESULT_set_LOCATOR VARYING;
    
     declare c2 cursor for s2;
     declare c_out cursor with return for s_out;
    
     declare EXIT HANDLER FOR SQLEXCEPTION
     BEGIN
       GET DIAGNOSTICS EXCEPTION 1 MSG = MESSAGE_TEXT;
       set RC = SQLCODE;
     END;
    
     set col_col=upper(col_col);
     set agg_col=upper(agg_col);
     -- insert result of select statement into temp table
     set stmt = 'describe '||sel_stmt;
     call SYSPROC.ADMIN_CMD(stmt);
     set stmt = '';
     associate result set locator (l1)
     with procedure SYSPROC.ADMIN_CMD;
     allocate c1 cursor for result set l1;
     --open c1;
     fetch c1 into SQLTYPE_ID, SQLTYPE, SQLLENGTH, SQLSCALE, SQLNAME_DATA;
     while (SQLCODE!=100) do
       set SQLTYPEF = SQLTYPE
       ||case 
          when SQLTYPE IN ('DECIMAL', 'DECFLOAT', 'CHARACTER', 'VARCHAR') then 
             '('||RTRIM(CHAR(SQLLENGTH))
           ||case when SQLTYPE='DECIMAL' then ','||RTRIM(CHAR(SQLSCALE)) else '' end
           ||')'                                
          else ''
         end;
       if (col_col=SQLNAME_DATA) then
         set apo = 
          case 
           when SQLTYPE in ('DECIMAL', 'DECFLOAT', 'INTEGER', 'SMALLINT', 'BIGINT', 'REAL', 'DOUBLE') then '' 
           else QUOT1 
          end;
       end if;
       set stmt = stmt||', '||SQLNAME_DATA||' '||SQLTYPEF;
       fetch c1 into SQLTYPE_ID, SQLTYPE, SQLLENGTH, SQLSCALE, SQLNAME_DATA;
     end while;
     close c1;
     set stmt = 
       'declare global temporary table '||tmp_tbl||'('||substr(stmt, 3)
     ||') with replace on commit preserve rows not logged';
     execute immediate stmt;
     set stmt = 'insert into '||tmp_tbl||' '||sel_stmt;
     execute immediate stmt;
    
     -- construct select statement
     set stmt = 'select distinct rtrim(char('||col_col||')) from '||tmp_tbl||' order by 1';
     prepare s2 from stmt;
     set stmt='';
     open c2;
     fetch c2 into col_val;
     while (SQLCODE!=100) do
       set stmt = 
         stmt||', '||agg_fn||'('
       ||'case when '||col_col||' '
       ||case when col_val is null then 'IS NULL' else ('='||apo||replace(col_val, QUOT1, QUOT1||QUOT1)||apo) end
       ||' then '||agg_col||' end) as '||QUOT2||coalesce(replace(col_val, QUOT2, QUOT2||QUOT2), null_ind)||QUOT2;
       fetch c2 into col_val;
     end while;
     close c2;
     -- add to the select statement groups
     set row_cols = nullif(row_cols, '');
     set stmt = 
       'select '||case when row_cols is not null then row_cols||',' else coalesce(row_cols, '') end
     ||substr(stmt, 2)||' from '||tmp_tbl||' '
     ||case when row_cols is not null then ('group by '||row_cols||' order by '||row_cols) else '' end;
     -- execute this statement
     prepare s_out from stmt;
     open c_out;
    END@
    

    参数说明:

    PARM DESC
    sel_stmt Any valid SELECT statement for source data generation
    row_cols Comma separated list of column names used in the GROUP BY of final statement
    col_col A column name to pivot
    agg_col A column name to aggregate
    agg_fn Any valid Db2 aggregation function for the column name in "agg_col" parameter
    tmp_tbl DGTT name for intermediate result
    null_ind Null indicator
    rc Return code (OUT)
    msg Message text (OUT)
    stmt The final SELECT generated (OUT)

    简要说明:
    tmp_tbl 中传递的 DGTT 被创建并填充了在 sel_stmt 中传递的 SELECT 语句的结果。在row_colscol_colagg_col 中指定的所有列名都必须在此语句的 SELECT 列表中。
    此 DGTT 上的最终 SELECT 语句是使用以下规则动态生成的:

    • 对于在col_col 中传递的列名称中的每个不同值Vx,将生成一个附加列表达式,如:, <agg_fn> (case when <col_col> = Vx then <agg_col> end) as "Vx", <agg_fn> (case when <col_col> IS NULL then <agg_col> end) as "<null_ind>" 用于Vx IS NULL),其中<parameter> 表示字符串使用此参数传递的值。
    • row_cols 列列表用于GROUP BY,如果它不为 NULL。

    用于您的案例:

    DECLARE GLOBAL TEMPORARY TABLE SESSION.MYTAB 
    (DATE_F, FILE_NM, ROW_ID, COLUMN_NM, COLUMN_VAL)
    AS
    (
    VALUES
      ('20200131', 'ABC.TXT', 1, 'ABC_COL1', '123')
    , ('20200131', 'ABC.TXT', 1, 'ABC_COL2', 'XYZ')
    , ('20201231', 'ABC.TXT', 2, 'ABC_COL1', '456')
    , ('20201231', 'ABC.TXT', 2, 'ABC_COL2', 'XY1')
    , ('20200630', 'XYZ.TXT', 1, 'XYZ_COL1', 'PQR')
    , ('20200630', 'XYZ.TXT', 1, 'XYZ_COL2', '567')
    , ('20200630', 'XYZ.TXT', 1, 'XYZ_COL3', 'MNO')
    ) WITH DATA WITH REPLACE ON COMMIT PRESERVE ROWS NOT LOGGED;
    

    第一次调用:

    call pivot
    (
      'select * from session.mytab where file_nm = ''ABC.TXT'''
    , 'DATE_F, ROW_ID'
    , 'COLUMN_NM'
    , 'COLUMN_VAL'
    , 'max'
    , 'session.tab1'
    , '-'
    , ?, ?, ?
    );
    
    DATE_F ROW_ID ABC_COL1 ABC_COL2
    20200131 1 123 XYZ
    20201231 2 456 XY1

    第二次调用:

    call pivot
    (
      'select * from session.mytab where file_nm = ''XYZ.TXT'''
    , 'DATE_F, ROW_ID'
    , 'COLUMN_NM'
    , 'COLUMN_VAL'
    , 'max'
    , 'session.tab1'
    , '-'
    , ?, ?, ?
    );
    
    DATE_F ROW_ID XYZ_COL1 XYZ_COL2 XYZ_COL3
    20200630 1 PQR 567 MNO

    【讨论】:

    • 谢谢马克。调用程序时出现错误。 SQL0104N 在“en COLUMN_VAL end)”之后发现了意外的标记“as”。预期的标记可能包括:“AND”。 SQLSTATE = 42601
    • 您能否按原样运行它,包括SESSION.MYTAB 表声明?如果你提供了准确的参数值,如果你使用一些不同的调用,那就太好了。
    • 我在使用 SESSION.MYTAB 时遇到同样的错误。 SQL0104N 在“then COLUMN_VAL end)”之后发现了意外的标记“as”
    • 你的 db2 版本和平台是什么?
    • 平台 Linux。 DB2 版本 11.1.0
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-22
    • 2022-01-20
    相关资源
    最近更新 更多