【问题标题】:Complex Query, LISTAGG, PIVOT复杂查询、LISTAGG、PIVOT
【发布时间】:2018-07-29 16:17:25
【问题描述】:

我有以下两种情况和所需的输出。不确定是否可以使用 LISTAGG 或 PIVOT。

在编写 SQL 查询以实现所需输出的最佳方式方面需要帮助。

这是我到目前为止所尝试的。但是,我没有考虑过 AUTH。我正在寻找考虑 AUTH 的查询,如果添加更多列,应该是可扩展的。

    SELECT CLM_TYPE_CSV,
     LISTAGG (PROGRAM, ',') WITHIN GROUP (ORDER BY PROGRAM)
        AS PROGRAM_CSV
    FROM (  SELECT PROGRAM,
           LISTAGG (CLAIM_TYPE, ',') WITHIN GROUP (ORDER BY CLAIM_TYPE)
              AS CLM_TYPE_CSV
          FROM STG_EDIT_DISP_PARAM
         WHERE ERR_NUM = '001'
      GROUP BY PROGRAM)
    GROUP BY CLM_TYPE_CSV

感谢帮助! --####场景1############

    ---------------------------------------------------------
    ERR_NUM |   PROGRAM |   CLAIM_TYPE  |   AUTH    |   
    ---------------------------------------------------------
    001     |    BLG    |   P           |     Y 
    001     |    ENG    |   O           |     Y 
    001     |    ENG    |   P           |     Y 
    001     |    FED    |   O           |     Y 
    001     |    FED    |   P           |     Y 
    001     |    FED    |   Q           |     Y     

期望的输出:

     --------------------------------------------------------
     ERR_NUM    |   PROGRAM    | CLAIM_TYPE   |    AUTH |
     --------------------------------------------------------
     001        |    BLG       |    P         |      Y
     001        |    ENG,FED   |    O,P       |      Y
     001        |    FED       |    Q         |      Y  

--####场景2############--------------------------- --

    ---------------------------------------------------------
    ERR_NUM       |    PROGRAM  |  CLAIM_TYPE  |    AUTH    |
    ---------------------------------------------------------
    001           |    BLG      |   P          |    N
    001           |    ENG      |   O          |    Y
    001           |    ENG      |   P          |    N
    001           |    FED      |   O          |    Y
    001           |    FED      |   P          |    Y
    001           |    FED      |   Q          |    Y   
    001           |    FED      |   X          |    N   

期望的输出:

    --------------------------------------------------------------------
    ERR_NUM     |  PROGRAM         | CLAIM_TYPE   | AUTH    |
    ------------------------------------------------------------------
    001         |    BLG,ENG       |    P         | N
    001         |    ENG,FED       |    O         | Y
    001         |    FED           |    P,Q       | Y
    001         |    FED           |    X         | N

【问题讨论】:

  • 到目前为止您尝试过哪些查询?请您编辑您的问题并添加它们好吗?

标签: sql oracle oracle12c oracle11gr2 listagg


【解决方案1】:

让我指导第一个场景(我希望你学习第二个场景)。

首先,您需要 LISTAGG 而不是 PIVOT

当您关注programclaim_type 列时,请分别考虑重复值和非重复值,如下面的解决方案(使用having(count(...)) 结构):

select err_num,
       listagg(program,',') within group (order by program) as program, 
       max(claim_type) as claim_type,
       auth
  from
(
  select err_num,program,
         min(claim_type)||','||max(claim_type) as claim_type,
         auth
    from tab
   where claim_type in 
  (
  select claim_type
    from tab
   group by claim_type 
   having count(claim_type)>1
  )
   group by program, err_num, auth 
   having count(program)>1
)  
group by claim_type, err_num, auth
union all
select err_num, program, claim_type, auth
  from tab
 where program in 
  (
    select program
      from tab
     group by program 
     having count(program)=1
  )
union all  
select err_num, program, claim_type, auth
  from tab
 where claim_type in 
  (
    select claim_type
      from tab
     group by claim_type 
     having count(claim_type)=1
  ) 
order by program, claim_type;   

SQL Fiddle Demo

【讨论】:

    【解决方案2】:

    所以,你想:

    • 按 err_num、auth 对行进行分组
    • 对于这些分组中的每个 claim_type,找出有多少程序
    • 返回在步骤 2 中计算的同一组中不同程序和声明类型的逗号分隔列表?

    如果是这样,您可以使用分析来计算每个 err_num、auth 和 claim_type 的程序计数。将此计数包含在您的最终分组依据中。

    要让 listagg 返回不同的值,如果程序和 claim_type 不是当前组中的第一个,则将它们映射为 null。您可以通过将值传递给 row_number 来做到这一点,当它为 1 时返回该列。否则为空。

    这给出了:

    create table tab (
      err_num varchar2(3), program varchar2(3), 
      claim_type varchar2(3), auth varchar2(3)
    );
    
    insert all 
       into tab values ('001','BLG','P','N') 
       into tab values ('001','ENG','O','Y')
       into tab values ('001','ENG','P','N')
       into tab values ('001','FED','O','Y') 
       into tab values ('001','FED','P','Y')
       into tab values ('001','FED','Q','Y')
       into tab values ('001','FED','X','N')
    select * from dual;   
    
    with rws as (
      select t.*, row_number () over ( order by err_num, program, claim_type ) rn,
             count (*) over ( partition by err_num, claim_type, auth ) grp_count
      from   tab t
    ), grps as (
      select r.*,
             case
               when row_number () over ( partition by claim_type, auth, grp_count order by 1 ) = 1 then
                 claim_type
             end claim_first,
             case
               when row_number () over ( partition by program, auth, grp_count order by 1 ) = 1 then
                 program
             end prog_first
      from   rws r 
    )
      select err_num, 
             listagg ( prog_first, ',' ) within group ( order by rn ) progs,
             listagg ( claim_first, ',' ) within group ( order by rn ) claims,
             auth
      from   grps
      group  by err_num, auth, grp_count;
    
    ERR_NUM   PROGS     CLAIMS   AUTH   
    001       FED       X        N      
    001       BLG,ENG   P        N      
    001       FED       P,Q      Y      
    001       ENG,FED   O        Y  
    

    向 Laurent Schneider 致敬,感谢他的许多独特的 listaggs 方法https://laurentschneider.com/wordpress/2014/05/distinct-listagg.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-18
      • 2013-06-28
      • 2012-06-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多