【问题标题】:Oracle SQL to convert rows to columnsOracle SQL 将行转换为列
【发布时间】:2021-03-14 15:21:46
【问题描述】:

我找不到合适的答案,所以我把它写在这里。我有一个包含以下字段的表格。

ID           Amount    DocNum   DocStatus   DueDate
AA           2400      00005     1          10-Jun-2019
AA           1400      00006     4          21-Sep-2019
AA           9000      00028     1          22-Aug-2020 
AA           5000      00201     2          31-Aug-2020
AA           6400      00410     1          22-Jan-2021
AA           2000      00511     1          01-Mar-2021
BB           1500      01390     1          01-Jan-2021

我想显示状态为 1 的前 3 个最新文档

ID Document1 Amount1 Document2 Amount2 Document3 Amount3 
AA 00511     2000    00410     6400    00028     9000 
BB 01390     1500    XX        XX      XX        XX

我以为我可以使用 Pivot 或 Decode 但无法确定其他条件。任何帮助表示赞赏。

【问题讨论】:

    标签: sql oracle pivot transpose


    【解决方案1】:

    您可以使用row_number() 和条件聚合:

    select id,
           max(case when seqnum = 1 then docnum end) as docnum_1,
           max(case when seqnum = 1 then amount end) as amount_1,
           max(case when seqnum = 2 then docnum end) as docnum_2,
           max(case when seqnum = 2 then amount end) as amount_2,
           max(case when seqnum = 3 then docnum end) as docnum_3,
           max(case when seqnum = 3 then amount end) as amount_3
    from (select t.*,
                 row_number() over (partition by id order by due_date desc) as seqnum
          from t
          where status = 1
         ) t
    group by id;
    

    【讨论】:

    • @mathguy 。 . .谢谢。
    【解决方案2】:
    alter session set nls_date_format='dd-Mon-yyyy';
    
    with
      my_table (id, amount, docnum, docstatus, duedate) as (
        select 'AA', 2400, '00005', 1, to_date('10-Jun-2019') from dual union all
        select 'AA', 1400, '00006', 4, to_date('21-Sep-2019') from dual union all
        select 'AA', 9000, '00028', 1, to_date('22-Aug-2020') from dual union all
        select 'AA', 5000, '00201', 2, to_date('31-Aug-2020') from dual union all
        select 'AA', 6400, '00410', 1, to_date('22-Jan-2021') from dual union all
        select 'AA', 2000, '00511', 1, to_date('01-Mar-2021') from dual union all
        select 'BB', 1500, '01390', 1, to_date('01-Jan-2021') from dual
      )
    select id, "1_DOC" as document1, "1_AMT" as amount1,
               "2_DOC" as document2, "2_AMT" as amount2,
               "3_DOC" as document3, "3_AMT" as amount3
    from   (
             select id, amount, docnum, 
                    row_number() over (partition by id 
                                       order by duedate desc) as rn
             from   my_table
             where  docstatus = 1
           )
    pivot  (min(docnum) as doc, min(amount) as amt for rn in (1, 2, 3))
    ;
    
    
    ID DOCUMENT1    AMOUNT1 DOCUMENT2    AMOUNT2 DOCUMENT3    AMOUNT3
    -- --------- ---------- --------- ---------- --------- ----------
    AA 00511           2000 00410           6400 00028           9000
    BB 01390           1500     
    

    您需要在子查询中完成所有准备工作:过滤docstatus = 1,按duedate 降序创建RN 排名,然后只选择您需要作为数据透视表的列。除了微不足道的旋转(在子查询中完成所有准备工作之后微不足道)之外,外部查询只需要在 select 子句中小心一点,以正确获取列名。

    【讨论】:

      【解决方案3】:

      您可以动态生成所需的 SQL SELECT 语句,以便通过使用 IN 参数创建这样的函数来表示前 2、3、4 ..etc 的行以显示是否前 2、3、4 ..etc并返回SYS_REFCURSOR 类型结果集为

      CREATE OR REPLACE FUNCTION Fn_Pivot_Doc_and_Amounts( numcol INT ) RETURN SYS_REFCURSOR IS
        v_recordset SYS_REFCURSOR;
        v_sql       VARCHAR2(32767);
        v_cols      VARCHAR2(32767);
      BEGIN
        SELECT LISTAGG( ''||level||' AS "'||level||'"' , ',' )
                       WITHIN GROUP ( ORDER BY level )
          INTO v_cols
          FROM dual
         CONNECT BY level <= numcol;
      
        v_sql :='SELECT *
                   FROM(SELECT id,docnum,amount,
                               ROW_NUMBER() OVER (PARTITION BY id ORDER BY duedate DESC) AS rn
                          FROM tab t
                         WHERE docstatus = 1)
                  PIVOT(
                        MAX(docnum) AS document,
                        MAX(amount) AS amount  FOR rn IN ( '|| v_cols ||'  )
                       )';
      
        OPEN v_recordset FOR v_sql;
        RETURN v_recordset;
      END;
      

      然后从 SQL Developer 的控制台调用

      SQL> DECLARE
             result SYS_REFCURSOR;
      BEGIN
         :result := Fn_Pivot_Doc_and_Amounts(2); -- 3,4,...
      END;
      /
      
      SQL> PRINT result;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-08
        • 2020-07-01
        • 2020-02-23
        • 2015-04-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多