Eliminate duplicates in ListAgg (Oracle)
You can use regular expressions and regexp_replace
to remove the duplicates after concatenation with listagg
:
SELECT Num1,
RTRIM(
REGEXP_REPLACE(
(listagg(Num2,'-') WITHIN GROUP (ORDER BY Num2) OVER ()),
'([^-]*)(-\1)+($|-)',
'\1\3'),
'-') Num2s
FROM ListAggTest;
This could be tidier if Oracle's regex flavour supported lookahead or non-capturing groups, but it doesn't.
However this solution does avoid scanning the source more than once.
DBFiddle here
As far as I can see, with the currently available language specification, this is
the shortest to achieve what you want if it must be done with listagg
.
select distinct
a.Num1,
b.num2s
from listaggtest a cross join (
select listagg(num2d, '-') within group (order by num2d) num2s
from (
select distinct Num2 num2d from listaggtest
)
) b;
What was your solution that was worse than the custom aggregate solution?
Although this is an old post with an accepted answer, I think the LAG() analytic function works well in this case and is noteworthy:
- LAG() removes duplicate values in column num2 with minimal expense
- No need for non-trivial regular expression to filter results
- Just one full table scan (cost=4 on simple example table)
Here is the proposed code:
with nums as (
SELECT
num1,
num2,
decode( lag(num2) over (partition by null order by num2), --get last num2, if any
--if last num2 is same as this num2, then make it null
num2, null,
num2) newnum2
FROM ListAggTest
)
select
num1,
--listagg ignores NULL values, so duplicates are ignored
listagg( newnum2,'-') WITHIN GROUP (ORDER BY Num2) OVER () num2s
from nums;
The results below appear to be what the OP desires:
NUM1 NUM2S
1 2-3-4-5-6
2 2-3-4-5-6
3 2-3-4-5-6
4 2-3-4-5-6
5 2-3-4-5-6
6 2-3-4-5-6