复杂查询,LISTAGG,PIVOT

时间:2018-07-29 16:17:25

标签: sql oracle oracle12c oracle11gr2 listagg

我有以下两种情况和所需的输出。不确定是否可以使用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

不胜感激!  -#### SCENARIO 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  

-#### SCENARIO 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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

因此,您要:

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

如果是,则可以使用分析功能来计算每个err_num,auth和Claim_type的程序计数。在您的最终分组依据中添加此计数。

要使listagg返回不同的值,如果程序和Claim_type不是当前组中的第一个,则将其映射为null。您可以通过将值传递给row_number,并在此值为1时返回该列来完成此操作。否则为null。

哪个给:

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