Comment trouver des enregistrements en double dans PostgreSQL


190

J'ai une table de base de données PostgreSQL appelée "user_links" qui autorise actuellement les champs en double suivants:

year, user_id, sid, cid

La contrainte unique est actuellement le premier champ « id », mais je suis maintenant d'ajouter une contrainte pour vous assurer que la year, user_id, sidetcid sont tous uniques , mais je ne peux pas appliquer la contrainte parce que les valeurs en double existent déjà qui violent cette contrainte.

Existe-t-il un moyen de trouver tous les doublons?


2
drs

Réponses:


335

L'idée de base sera d'utiliser une requête imbriquée avec une agrégation de comptage:

select * from yourTable ou
where (select count(*) from yourTable inr
where inr.sid = ou.sid) > 1

Vous pouvez ajuster la clause where dans la requête interne pour restreindre la recherche.


Il existe une autre bonne solution pour ce qui est mentionné dans les commentaires (mais tout le monde ne les lit pas):

select Column1, Column2, count(*)
from yourTable
group by Column1, Column2
HAVING count(*) > 1

Ou plus court:

SELECT (yourTable.*)::text, count(*)
FROM yourTable
GROUP BY yourTable.*
HAVING count(*) > 1

65
Vous pouvez également utiliser HAVING:select co1, col2, count(*) from tbl group by col1, col2 HAVING count(*)>1
alexkovelsky

1
Merci @alexkovelsky, la déclaration a été plus facile à modifier pour moi et a été plus rapide. Je suggérerais une réponse avec elle pour une meilleure visibilité.
Vesanto

ces options ont fonctionné pour moi, les autres regroupent les résultats, et ces options m'ont donné tous les enregistrements dupliqués au lieu de simplement l'enregistrement dupliqué, merci!
rome3ro

1
J'ai votre réponse pour être un peu lente. Sur une table de 10 000 lignes * 18 colonnes, la requête a pris 8 secondes
aydow

1
c'est la confiture juste là bro. diable ouais. Merci. 💯
dps

91

De " Rechercher les lignes en double avec PostgreSQL ", voici la solution intelligente:

select * from (
  SELECT id,
  ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id asc) AS Row
  FROM tbl
) dups
where 
dups.Row > 1

11
C'est rapide! Travaillé sur des millions de lignes en une fraction de seconde. D'autres réponses viennent d'être accrochées là-bas ...
dmvianna

5
Aussi vite que je vois, cette requête ne considère pas toutes les lignes d'un groupe. Il ne montre que les doublons de quelque chose, une partie des doublons sera avec rownum = 1. Corrigez-moi si je me trompe
Vladimir Filipchenko

9
@vladimir Filipchenko Pour l'avoir avec toutes les lignes, ajoutez un niveau à la solution Alexkovelsky:SELECT * FROM ( SELECT *, LEAD(row,1) OVER () AS nextrow FROM ( SELECT *, ROW_NUMBER() OVER(w) AS row FROM tbl WINDOW w AS (PARTITION BY col1, col2 ORDER BY col3) ) x ) y WHERE row > 1 OR nextrow > 1;
Le Droid

4
@VladimirFilipchenko Remplacez simplement ROW_NUMBER()par COUNT(*)et ajoutez rows between unbounded preceding and unbounded followingaprèsORDER BY id asc
alexkovelsky

2
tellement mieux que les autres solutions que j'ai trouvées. fonctionne également aussi bien pour la suppression des dupes avec DELETE ...USINGquelques ajustements mineurs
Brandon

6

Vous pouvez rejoindre la même table sur les champs qui seraient dupliqués puis anti-jointure sur le champ id. Sélectionnez le champ id du premier alias de table (tn1), puis utilisez la fonction array_agg sur le champ id du second alias de table. Enfin, pour que la fonction array_agg fonctionne correctement, vous regrouperez les résultats par le champ tn1.id. Cela produira un jeu de résultats contenant l'identifiant d'un enregistrement et un tableau de tous les identifiants correspondant aux conditions de jointure.

select tn1.id,
       array_agg(tn2.id) as duplicate_entries, 
from table_name tn1 join table_name tn2 on 
    tn1.year = tn2.year 
    and tn1.sid = tn2.sid 
    and tn1.user_id = tn2.user_id 
    and tn1.cid = tn2.cid
    and tn1.id <> tn2.id
group by tn1.id;

De toute évidence, les identifiants qui seront dans le tableau duplicate_entries pour un identifiant auront également leurs propres entrées dans le jeu de résultats. Vous devrez utiliser cet ensemble de résultats pour décider quel identifiant vous voulez devenir la source de la «vérité». Le seul enregistrement qui ne devrait pas être supprimé. Vous pourriez peut-être faire quelque chose comme ceci:

with dupe_set as (
select tn1.id,
       array_agg(tn2.id) as duplicate_entries, 
from table_name tn1 join table_name tn2 on 
    tn1.year = tn2.year 
    and tn1.sid = tn2.sid 
    and tn1.user_id = tn2.user_id 
    and tn1.cid = tn2.cid
    and tn1.id <> tn2.id
group by tn1.id
order by tn1.id asc)
select ds.id from dupe_set ds where not exists 
 (select de from unnest(ds.duplicate_entries) as de where de < ds.id)

Sélectionne le plus petit nombre d'ID ayant des doublons (en supposant que l'ID augmente int PK). Ce seraient les pièces d'identité que vous conserveriez.


3

Afin de faciliter les choses, je suppose que vous souhaitez appliquer une contrainte unique uniquement pour l'année de la colonne et la clé primaire est une colonne nommée id.

Afin de trouver des valeurs en double, vous devez exécuter,

SELECT year, COUNT(id)
FROM YOUR_TABLE
GROUP BY year
HAVING COUNT(id) > 1
ORDER BY COUNT(id);

En utilisant l'instruction sql ci-dessus, vous obtenez une table qui contient toutes les années en double dans votre table. Afin de supprimer tous les doublons à l'exception de la dernière entrée en double, vous devez utiliser l'instruction sql ci-dessus.

DELETE
FROM YOUR_TABLE A USING YOUR_TABLE_AGAIN B
WHERE A.year=B.year AND A.id<B.id;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.