1327 votes

Comment puis-je supprimer les lignes en double ?

Quelle est la meilleure façon de supprimer les lignes en double d'un fichier assez volumineux ? SQL Server (c'est-à-dire 300 000+ lignes) ?

Bien entendu, les lignes ne seront pas des doublons parfaits en raison de l'existence de l'élément RowID le champ d'identité.

MaTable

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null

14 votes

Un conseil rapide pour les utilisateurs de PostgreSQL qui lisent ceci (beaucoup, à en juger par la fréquence des liens) : Pg n'expose pas les termes CTE en tant que vues actualisables. DELETE FROM un terme CTE directement. Voir stackoverflow.com/q/18439054/398670

0 votes

@CraigRinger la même chose est vraie pour Sybase - J'ai rassemblé les solutions restantes ici (elles devraient être valables pour PG et d'autres, aussi : stackoverflow.com/q/19544489/1855801 (il suffit de remplacer le ROWID() par la colonne RowID, le cas échéant)

13 votes

Juste pour ajouter une mise en garde ici. Lorsque vous exécutez un processus de déduplication, vérifiez toujours d'abord ce que vous supprimez ! C'est l'un des domaines où il est très fréquent de supprimer accidentellement de bonnes données.

1168voto

Mark Brackett Points 46824

En supposant qu'il n'y ait pas de nulles, vous GROUP BY les colonnes uniques, et SELECT le site MIN (or MAX) RowId comme la ligne à conserver. Ensuite, il suffit de supprimer tout ce qui n'a pas d'ID de rangée :

DELETE FROM MyTable
LEFT OUTER JOIN (
   SELECT MIN(RowId) as RowId, Col1, Col2, Col3 
   FROM MyTable 
   GROUP BY Col1, Col2, Col3
) as KeepRows ON
   MyTable.RowId = KeepRows.RowId
WHERE
   KeepRows.RowId IS NULL

Dans le cas où vous avez un GUID au lieu d'un entier, vous pouvez remplacer

MIN(RowId)

con

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))

340 votes

Cela fonctionnerait-il aussi bien ? DELETE FROM MyTable WHERE RowId NOT IN (SELECT MIN(RowId) FROM MyTable GROUP BY Col1, Col2, Col3);

1 votes

Excellente solution ! Il semble que pour PostgreSQL, il faille une sous-requête de plus comme dans gist.github.com/754805

0 votes

@Georg : Je pense que oui. Votre solution est plus courte et plus claire. Je ne suis pas sûr des performances, peut-être qu'elle est équivalente à celle de Mark, mais avec des tables vraiment grandes, je m'en tiendrais probablement au LEFT JOIN.

777voto

Martin Smith Points 174101

Une autre façon de procéder est la suivante

; 

--Ensure that any immediately preceding statement is terminated with a semicolon above
WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3 
                                       ORDER BY ( SELECT 0)) RN
         FROM   #MyTable)
DELETE FROM cte
WHERE  RN > 1;

J'utilise ORDER BY (SELECT 0) ci-dessus car le choix de la ligne à préserver en cas d'égalité est arbitraire.

Pour conserver le dernier en date dans RowID par exemple, vous pourriez utiliser ORDER BY RowID DESC

Plans d'exécution

Le plan d'exécution est souvent plus simple et plus efficace que celui de la réponse acceptée, car il ne nécessite pas de jointure automatique.

Execution Plans

Ce n'est cependant pas toujours le cas. Un endroit où le GROUP BY peut être préférée dans les situations où une agrégat de hachage serait choisi de préférence à un agrégat de flux.

En ROW_NUMBER donnera toujours à peu près le même plan, alors que la solution de la GROUP BY est plus flexible.

Execution Plans

Les facteurs qui pourraient favoriser l'approche de l'agrégat de hachage seraient les suivants

  • Pas d'index utile sur les colonnes de partitionnement
  • relativement moins de groupes avec relativement plus de doublons dans chaque groupe

Dans les versions extrêmes de ce deuxième cas (s'il y a très peu de groupes avec beaucoup de doublons dans chacun d'entre eux), on peut aussi envisager d'insérer simplement les lignes à conserver dans une nouvelle table, alors TRUNCATE -sur l'original et les recopier pour minimiser la journalisation par rapport à la suppression d'une très grande partie des lignes.

31 votes

Si je peux ajouter : La réponse acceptée ne fonctionne pas avec les tables qui utilisent uniqueidentifier . Celui-ci est beaucoup plus simple et fonctionne parfaitement sur n'importe quelle table. Merci Martin.

1 votes

C'est la seule solution qui fonctionne sur ma grande table (30 millions de lignes). J'aimerais pouvoir lui donner plus de +1

15 votes

C'est une réponse tellement géniale ! Cela a fonctionné alors que j'avais supprimé l'ancien PK avant de réaliser qu'il y avait des doublons. +100

153voto

Jon Galloway Points 28243

Il y a un bon article sur suppression des doublons sur le site du support Microsoft. C'est assez conservateur - ils vous demandent de tout faire en plusieurs étapes - mais cela devrait bien fonctionner pour les grandes tables.

J'ai utilisé des auto-joints pour faire cela dans le passé, bien que cela puisse probablement être amélioré avec une clause HAVING :

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField

0 votes

Parfait ! j'ai trouvé que c'est le moyen le plus efficace pour supprimer les lignes en double sur mon ancienne version mariadb 10.1.xx. merci !

0 votes

Beaucoup plus simple et plus facile à comprendre !

0 votes

J'ai un doute, dans votre requête sql, pourquoi n'utilisez-vous pas le mot clé 'From' après 'DELETE' ? Je l'ai vu dans de nombreuses autres solutions.

100voto

gngolakia Points 654

La requête suivante est utile pour supprimer les lignes en double. La table dans cet exemple a ID comme une colonne d'identité et les colonnes qui ont des données en double sont Column1 , Column2 y Column3 .

DELETE FROM TableName
WHERE  ID NOT IN (SELECT MAX(ID)
                  FROM   TableName
                  GROUP  BY Column1,
                            Column2,
                            Column3
                  /*Even if ID is not null-able SQL Server treats MAX(ID) as potentially
                    nullable. Because of semantics of NOT IN (NULL) including the clause
                    below can simplify the plan*/
                  HAVING MAX(ID) IS NOT NULL) 

Le script suivant montre l'utilisation de la fonction GROUP BY , HAVING , ORDER BY en une seule requête, et renvoie les résultats avec la colonne dupliquée et son nombre.

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC

1 votes

Erreur MySQL avec le premier script 'Vous ne pouvez pas spécifier la table cible 'TableName' pour la mise à jour dans la clause FROM'.

0 votes

Outre l'erreur déjà signalée par D.Rosado, votre première requête est également très lente. La requête SELECT correspondante a pris sur mon installation +- 20 fois plus de temps que la réponse acceptée.

8 votes

@parvus - La question est étiquetée SQL Server et non MySQL. La syntaxe est bonne dans SQL Server. De plus, MySQL est notoirement mauvais pour l'optimisation des sous-requêtes. voir par exemple ici . Cette réponse convient parfaitement à SQL Server. En effet NOT IN est souvent plus performant que OUTER JOIN ... NULL . J'ajouterais un HAVING MAX(ID) IS NOT NULL à la requête même si, sémantiquement, cela ne devrait pas être nécessaire car cela peut améliorer le plan. exemple de cela ici

67voto

SoftwareGeek Points 2899
delete t1
from table t1, table t2
where t1.columnA = t2.columnA
and t1.rowid>t2.rowid

Postgres :

delete
from table t1
using table t2
where t1.columnA = t2.columnA
and t1.rowid > t2.rowid

0 votes

Pourquoi poster une solution Postgres sur une question SQL Server ?

3 votes

@Lankymart Parce que les utilisateurs de postgres viennent ici aussi. Regardez le score de cette réponse.

2 votes

J'ai vu cela dans certaines questions SQL populaires, comme dans aquí , aquí y aquí . Le PO a obtenu sa réponse et tous les autres ont reçu de l'aide. Pas de problème, IMHO.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X