Yanick Salzmann
Yanick Salzmann

Reputation: 1498

Oracle SQL aggregate rows into column listagg with condition

I am having the following - simplified - layout for tables:

content.blockId is a foreign key to blocks.id. The idea is that in the content table you have many content entries with different types for one block.

I am now looking for a query that can provide me with an aggregation based on a blockId where all the content entries of the 3 different types are concatenated and put into respective columns.

I have already started and found the listagg function which is working well, I did the following statement and lists me all the content entries in a column:

SELECT listagg(c.data, ',') WITHIN GROUP (ORDER BY c.order) FROM content c WHERE c.blockId = 330;

Now the concatenated string however contains all the data elements of the block in one column. What I would like to achieve is that its put into separate columns based on the type. For example the following content of content would be like this:

Now I wanted to get as an output 2 columns, one is FRAGMENT and one is BULK, where FRAGMENT contains "content1;content3;content4" and BULK contains "content2"

Is there an efficient way of achieving this?

Upvotes: 4

Views: 13888

Answers (2)

SomeJavaGuy
SomeJavaGuy

Reputation: 7347

As an alternative, if you want it more dynamic, you could pivot the outcome. Note, that this will only work for Oracle 11.R2. Here´s an example how it could look like:

select * from
(with dataSet as (select 1 idV, 1 bulkid, 0 orderV, 'content1' dataV, 'FRAGMENT' typeV from dual union
                 select 2, 1, 1, 'content2', 'BULK' from dual union
                 select 3, 1, 3, 'content4', 'FRAGMENT' from dual union
                 select 4, 1, 2, 'content3', 'FRAGMENT' from dual)
select typeV, listagg(dataSet.dataV ,',')  WITHIN GROUP (ORDER BY orderV) OVER (PARTITION BY typeV) dataV from dataSet)
pivot
(
   max(dataV)
   for typeV in ('BULK', 'FRAGMENT')
)

O/P

Bulk      |  FRAGMENT
-----------------
content2  |  content1,content3,content4

The important things here:

OVER (PARTITION BY typeV): this acts like a group by for the listagg, concatinating everything having the same typeV.

for typeV in ('BULK', 'FRAGMENT'): this will gather the data for BULK and FRAGMENT and produce separate columns for each.

max(dataV) simply to provide a aggregate function, otherwise pivot wont work.

Upvotes: 2

Gordon Linoff
Gordon Linoff

Reputation: 1269633

You can use case:

SELECT listagg(CASE WHEN content = 'FRAGMENT' THEN c.data END, ',') WITHIN GROUP (ORDER BY c.order) as fragments,
       listagg(CASE WHEN content = 'BULK' THEN c.data END, ',') WITHIN GROUP (ORDER BY c.order) as bulks
FROM content c
WHERE c.blockId = 330;

Upvotes: 7

Related Questions