How to remove duplicate values from a aggregate list when using LISTAGG() in oracle SQL?
For a simple query, you can use a subquery:
SELECT GRADE, listagg(name, ';') within group (order by name) as names
FROM (SELECT DISTINCT GRADE, st_name || ';' || st_last_name as name
FROM STUDENTS
) s
GROUP BY GRADE;
I find it odd that you are using a semi-colon both to separate st_name
and st_last_name
and to separate the different values. I would expect different separators to be used -- 'john;smith;jane;doe;'
is harder to read than 'john,smith;jane,doe'
.
How I remove duplicates from listagg
First select DISTINCT values you need, then apply LISTAGG to them. Here's an example based on Scott's schema.
SQL> -- Duplicate jobs within the department
SQL> select deptno, listagg(job, ', ') within group (order by job) jobs
2 from emp
3 group by deptno;
DEPTNO JOBS
---------- ------------------------------------------------------------
10 CLERK, MANAGER, PRESIDENT
20 ANALYST, ANALYST, CLERK, CLERK, MANAGER
30 CLERK, MANAGER, SALESMAN, SALESMAN, SALESMAN, SALESMAN
SQL>
SQL> -- This won't work - DISTINCT can't be used in LISTAGG
SQL> select deptno, listagg(distinct job, ', ') within group (order by job) jobs
2 from emp
3 group by deptno;
select deptno, listagg(distinct job, ', ') within group (order by job) jobs
*
ERROR at line 1:
ORA-30482: DISTINCT option not allowed for this function
SQL>
SQL> -- So - select distinct jobs first, then apply LISTAGG to it
SQL> select x.deptno, listagg(x.job, ', ') within group (order by x.job) jobs
2 from (select distinct deptno, job
3 from emp) x
4 group by x.deptno;
DEPTNO JOBS
---------- ------------------------------------------------------------
10 CLERK, MANAGER, PRESIDENT
20 ANALYST, CLERK, MANAGER
30 CLERK, MANAGER, SALESMAN
SQL>
Oracle SQL: How to remove duplicate in listagg
Maybe just create the SUM of the SNR/SMR/Membrane columns, group them by name, and replace the numbers with the strings that you want to see in the output.
Query (first step ...)
select name
, sum( snr_column ), sum( smr_column ), sum( membrane_column )
from original
group by name
;
-- output
NAME SUM(SNR_COLUMN) SUM(SMR_COLUMN) SUM(MEMBRANE_COLUMN)
2 1 1 2
3 null 2 1
1 2 2 null
Replace the sums, concatenate, remove the trailing comma with RTRIM()
select
name
, rtrim(
case when sum( snr_column ) >= 1 then 'SNR, ' end
|| case when sum( smr_column ) >= 1 then 'SMR, ' end
|| case when sum( membrane_column ) >= 1 then 'Membrane' end
, ' ,'
) as technology
from original
group by name
order by name
;
-- output
NAME TECHNOLOGY
1 SNR, SMR
2 SNR, SMR, Membrane
3 SMR, Membrane
Code the CASEs in the required order.
DBfiddle
SQL listagg and concat, remove duplicates
A runner can have multiple nationalities and participate in multiple races. You join the two tables, although a particular nationality isn't linked to a particular race. Thus you are generating an undesired cartesian product. The problem is hence that you are joining entities that are not completely related.
Joining all involved tables and then aggregate the inflated intermediate result is a commom mistake. Instead aggregate first in order to get to the entities that you really want to join.
What you want to do is show a runner with their race list and their nationality list. Create these lists and then join them:
select
c.n_coureur,
c.nom,
c.prenom,
c.annee_prem,
c.annee_naissance,
pc.participations,
an.nationalites
from tdf_coureur c
left join
(
select
n_coureur,
listagg(annee || '|' || n_equipe || '|' || n_sponsor || '|' || n_dossard || '|' || jeune || '|' || valide, ';')
within group (order by n_coureur, nom, prenom, annee_prem, annee_naissance) as participations
from tdf_parti_coureur
group by n_coureur
) pc using(n_coureur)
left join
(
select
n_coureur,
listagg(code_cio || '|' || annee_debut || '|' || annee_fin, ';')
within group (order by n_coureur, nom, prenom, annee_prem, annee_naissance) as nationalites
from tdf_app_nation
group by n_coureur
) an using(n_coureur)
order by c.n_coureur;
Oracle SQL Listagg remove duplicates with case statement conditions
LISTAGG
is an aggregate function. If you apply it to a column, then you need to specify in the query what columns you're grouping by. Typically that is all the columns that don't have an aggregate function.
I didn't test since there is no sample data for the dept table nor the person_roles table but this is probably the issue
SELECT p.person, r.role as myrole, listagg(d.dept, ', ') within group (order by d.dept) as dept_list,
CASE
WHEN rl.role_type = 1 AND r.role IN ('AAA','BBB') THEN 'Add'
WHEN rl.role_type = 0 AND r.role = 'Auth' THEN 'Remove'
END as myaccess
FROM person_role pr
JOIN person p ON p.id = pr.person_id
JOIN myrole r ON r.id = pr.role_id
JOIN (
SELECT p.id, MIN(CASE WHEN r.ROLE = 'Auth' THEN 0 WHEN r.ROLE in ('AAA','BBB') THEN 1 ELSE 2 END) as role_type
FROM person_role pr
JOIN person p ON p.id = pr.person_id
JOIN myrole r ON r.id = pr.role_id
GROUP BY p.id
) rl ON rl.id = pr.person_id
left join dept d on d.id = pr.dept_id
GROUP BY
p.person,
r.role,
CASE
WHEN rl.role_type = 1 AND r.role IN ('AAA','BBB') THEN 'Add'
WHEN rl.role_type = 0 AND r.role = 'Auth' THEN 'Remove'
END
ORDER BY p.person
How to eliminate duplicates from multiple LISTAGG 'ed columns in Oracle
You can do this by taking advantage of the fact the listagg()
ignores NULL
values. So, use row_number()
to enumerate duplicates and then just pull in the first value:
SELECT grantee,
LISTAGG(CASE WHEN seqnum_granted_role = 1 THEN granted_role END, '-')
WITHIN GROUP (ORDER BY granted_role) AS granted_role,
LISTAGG(CASE WHEN seqnum_privilege = 1 THEN privilege END, '-')
WITHIN GROUP (ORDER BY privilege) AS privs,
owner,
table_name
FROM (SELECT grantee, granted_role, privilege,
owner, table_name,
ROW_NUMBER() OVER (PARTITION BY grantee, owner, table_name, granted_role
ORDER BY grantee
) as seqnum_granted_role,
ROW_NUMBER() OVER (PARTITION BY grantee, owner, table_name, privilege
ORDER BY grantee
) as seqnum_privilege
FROM sys.privs
) p
GROUP BY grantee, owner, table_name;
Remove duplicates from an already aggregated LISTAGG using LISTAGG
You can use the below to unlistagg and remove the duplicate and then listagg again.The DB Fiddle here
WITH data
AS (SELECT id,
Listagg(products, '|')
within GROUP (ORDER BY products) PRODUCTS,
SUM(amount) SUM_AMT
FROM (SELECT *
FROM table_one
UNION
SELECT *
FROM table_two)
GROUP BY id),
d2
AS (SELECT DISTINCT id,
Regexp_substr(products, '[^|]+', 1, column_value) AS
products,
sum_amt
FROM data
cross join TABLE(Cast(MULTISET (SELECT LEVEL
FROM dual
CONNECT BY LEVEL <=
Regexp_count(products,
'[^|]+'))
AS
sys.ODCINUMBERLIST)))
SELECT id,
Listagg(products, '|')
within GROUP (ORDER BY id) PRODUCTS,
sum_amt
FROM d2
GROUP BY id,
sum_amt;
Related Topics
How to Make a Stored Procedure Return a "Dataset" Using a Parameter I Pass
Parsing Openxml with Multiple Elements of the Same Name
Datename(Month,Getadate()) Is Returning Numeric Value of the Month as '09'
Can Scalar Functions Be Applied Before Filtering When Executing a SQL Statement
Does SQL Server Optimize Dateadd Calculation in Select Query
Difference Between a Inline Function and a View
How to Prevent Ssis from Writing Column Names to the Flat File Output
SQL Script to Change All Table References in All Stored Procedures
How to Use a Function-Based Index on a Column That Contains Nulls in Oracle 10+
Do Clustered Index on a Column Guarantees Returning Sorted Rows According to That Column
Local Collection Types Not Allowed in SQL Statements
Good Database and Structure to Store Synonyms
Escaping Command Parameters Passed to Xp_Cmdshell to Dtexec
Why Oracle Is Saying Not a Group by Expression
Calculating Age from Birthday with Oracle Plsql Trigger and Insert the Age in Table