Search code examples
sqloracle-databaseoracle11gstring-concatenationlistagg

concate or segregate strings based on requirement


USER_ID     COLUMN1       COLUMN2

JOHN          24           CA
JOHN          24           LA
JOHN          63           CA
JOHN          63           LA
JOHN          66           CA
JOHN          66           LA
JOHN           9           AF
JOHN           9           AL
JOHN           9           AW
JOHN           9           DF

Required output:

USER_ID          RESULT

JOHN             24~CA-LA  +  63~CA-LA  + 66~CA-LA +  9~AF-AL-AW-DF

This is my requirement. I am trying listagg():

select USER_ID,
   (listagg(case when seqnum_p = 1 then COLUMN1 end, '-') within group (order by COLUMN1) ||
    '~' ||
    listagg(case when seqnum_b = 1 then COLUMN2 end, '-') within group (order by COLUMN2)
   ) as result
from (select TABLE.*,
         row_number() over (partition by USER_ID, COLUMN1 order by COLUMN1) as seqnum_p,
         row_number() over (partition by USER_ID, COLUMN2 order by COLUMN2) as seqnum_b
  from TABLE
 )
group by USER_ID;

Current output:

JOHN         ||       AF-AL-AW-CA-DF-LA~24-63-66-9

Solution

  • You can do two levels of aggregation instead of dealing with the row numbers:

    select user_id,
      listagg(tmp, ' + ') within group (order by tmp) as result
    from (
      select user_id,
        column1 ||'~'|| listagg(column2, '-') within group (order by column2) as tmp
      from your_table
      group by user_id, column1
    )
    group by user_id
    order by user_id;
    
    USER RESULT                                            
    ---- --------------------------------------------------
    JOHN 24~CA-LA + 63~CA-LA + 66~CA-LA + 9~AF-AL-AW-DF    
    

    The inner query gives you the first level:

    USER TMP                                               
    ---- --------------------------------------------------
    JOHN 9~AF-AL-AW-DF                                     
    JOHN 24~CA-LA                                          
    JOHN 63~CA-LA                                          
    JOHN 66~CA-LA                                          
    

    and the outer level further aggregates those into a single string per user.

    The order-by in the outer query aggregation is of a string starting with a number, which puts '9~...' after '24~...', which would normally be odd but seems to be what you expect.

    If you actually wanted them in numerical column-1-order you can include that in the subquery and use it for ordering:

    select user_id,
      listagg(tmp, ' + ') within group (order by column1) as result
    from (
      select user_id, column1,
        column1 ||'~'|| listagg(column2, '-') within group (order by column2) as tmp
      from your_table
      group by user_id, column1
    )
    group by user_id
    order by user_id;
    
    USER RESULT                                            
    ---- --------------------------------------------------
    JOHN 9~AF-AL-AW-DF + 24~CA-LA + 63~CA-LA + 66~CA-LA