Home > Back-end >  Oracle SQL: length limitation of LISTAGG with CASE
Oracle SQL: length limitation of LISTAGG with CASE

Time:10-01

I use listaggcombined with case when as aggretation as follows:

Select 
date as "Some Date",
sum(case when status = 'active' then 1 else 0 end) as "active id numbers",
listagg(case when status = 'active' then id_number END, ',') within group (order by id_number) as "List of active id numbers"
from mytable
group by date;

Result:

Date Active id numbers List of active id numbers
01/01/2021 4 2334,123,5123,13
01/02/2021 6 4213,14123,13,1235,126,745
01/03/2021 2 416,754

It works like a charm, unless the result is too long, as listaggis limited to 4000 bytes.

I cannot use on overflow truncate to truncate the listagg resultset, as I really need the full result.

In LISTAGG function: "result of string concatenation is too long" it was suggested to generally replace listagg with xmlagg, to receive a CLOB instead, which would work for me.

However, I do not manage to correctly replace my listagg with xmlagg, as the case when statement does not seem to work.

This does not work: (error: 'missing keyword')

Select 
date as "Some Date",
sum(case when status = 'active' then 1 else 0 end) as "active id numbers",
RTRIM(XMLAGG(XMLELEMENT(E,case when status = 'active' then id_number,',').EXTRACT('//text()') ORDER BY id_number).GetClobVal(),',') AS LIST
from mytable
group by date;

Could you advice me on how to work out the listagg limitations in my case? Will xmlagg work at all with the case when statement?

Thanks in advance

CodePudding user response:

A simple option might be to use your current query - with CASEs but without aggregations - as a "source" for query which actually does aggregations on values that are already prepared through CASE. Something like this:

WITH
   temp
   AS
      -- your current query, without aggregations
      (SELECT datum,
              CASE WHEN status = 'active' THEN 1 ELSE 0 END status,
              CASE WHEN status = 'active' THEN id_number END id_number
         FROM mytable)
  SELECT datum,
         SUM (status) AS active_id_numbers,
         RTRIM (
            XMLAGG (XMLELEMENT (e, id_number, ',').EXTRACT ('//text()')
                    ORDER BY id_number).getclobval (),
            ',') AS list
    FROM temp
GROUP BY datum

CodePudding user response:

You are missing the END keyword for the CASE expression:

Select date_column as "Some Date",
       sum(case when status = 'active' then 1 else 0 end) as "active id numbers",
       RTRIM(
         XMLAGG(
           XMLELEMENT(
             E,
             case when status = 'active' then id_number END,  -- Here
             ','
           ).EXTRACT('//text()')
           ORDER BY id_number
         ).GetClobVal(),
         ','
       ) AS LIST
from   mytable
group by date_column;

However, you could just add a WHERE filter:

Select date_column as "Some Date",
       COUNT(*) as "active id numbers",
       RTRIM(
         XMLAGG(
           XMLELEMENT(
             E,
             id_number,
             ','
           ).EXTRACT('//text()')
           ORDER BY id_number
         ).GetClobVal(),
         ','
       ) AS LIST
from   mytable
where  status = 'active'
group by date_column;

db<>fiddle here

  • Related