web-dev-qa-db-fra.com

Comment supprimer les doublons sur une table MySQL?

J'ai besoin de DELETE lignes dupliquées pour sid spécifié sur unMySQLtable.

Comment puis-je faire cela avec une requête SQL?

DELETE (DUPLICATED TITLES) FROM table WHERE SID = "1"

Quelque chose comme ça, mais je ne sais pas comment le faire.

139
Ali Poder

cela supprime les doublons en place, sans créer de nouvelle table

ALTER IGNORE TABLE `table_name` ADD UNIQUE (title, SID)

remarque: ne fonctionne bien que si l'index tient dans la mémoire

199
user187291

Supposons que vous ayez une table employee, avec les colonnes suivantes:

employee (first_name, last_name, start_date)

Afin de supprimer les lignes avec une colonne dupliquée first_name:

delete
from employee using employee,
    employee e1
where employee.id > e1.id
    and employee.first_name = e1.first_name  
112
Abhijoy_D

Suite à supprimer les doublons pour tous les SID-s, pas seulement un seul.

Avec table de temp

CREATE TABLE table_temp AS
SELECT * FROM table GROUP BY title, SID;

DROP TABLE table;
RENAME TABLE table_temp TO table;

Puisque temp_table est fraîchement créé, il n’a pas d’index. Vous devrez les recréer après avoir supprimé les doublons. Vous pouvez vérifier quels index vous avez dans la table avec SHOW INDEXES IN table

Sans table de temp:

DELETE FROM `table` WHERE id IN (
  SELECT all_duplicates.id FROM (
    SELECT id FROM `table` WHERE (`title`, `SID`) IN (
      SELECT `title`, `SID` FROM `table` GROUP BY `title`, `SID` having count(*) > 1
    )
  ) AS all_duplicates 
  LEFT JOIN (
    SELECT id FROM `table` GROUP BY `title`, `SID` having count(*) > 1
  ) AS grouped_duplicates 
  ON all_duplicates.id = grouped_duplicates.id 
  WHERE grouped_duplicates.id IS NULL
)
53
Kamil Szot

Suppression de lignes en double dans MySQL, procédure pas à pas

Crée la table et insère quelques lignes:

dev-db> create table penguins(foo int, bar varchar(15), baz datetime);
Query OK, 0 rows affected (0.07 sec)
dev-db> insert into penguins values(1, 'skipper', now());
dev-db> insert into penguins values(1, 'skipper', now());
dev-db> insert into penguins values(3, 'kowalski', now());
dev-db> insert into penguins values(3, 'kowalski', now());
dev-db> insert into penguins values(3, 'kowalski', now());
dev-db> insert into penguins values(4, 'rico', now());
Query OK, 6 rows affected (0.07 sec)
dev-db> select * from penguins;
+------+----------+---------------------+
| foo  | bar      | baz                 |
+------+----------+---------------------+
|    1 | skipper  | 2014-08-25 14:21:54 |
|    1 | skipper  | 2014-08-25 14:21:59 |
|    3 | kowalski | 2014-08-25 14:22:09 |
|    3 | kowalski | 2014-08-25 14:22:13 |
|    3 | kowalski | 2014-08-25 14:22:15 |
|    4 | rico     | 2014-08-25 14:22:22 |
+------+----------+---------------------+
6 rows in set (0.00 sec)

Ensuite, supprimez les doublons:

dev-db> delete a
    -> from penguins a
    -> left join(
    -> select max(baz) maxtimestamp, foo, bar
    -> from penguins
    -> group by foo, bar) b
    -> on a.baz = maxtimestamp and
    -> a.foo = b.foo and
    -> a.bar = b.bar
    -> where b.maxtimestamp IS NULL;
Query OK, 3 rows affected (0.01 sec)

Résultat:

dev-db> select * from penguins;
+------+----------+---------------------+
| foo  | bar      | baz                 |
+------+----------+---------------------+
|    1 | skipper  | 2014-08-25 14:21:59 |
|    3 | kowalski | 2014-08-25 14:22:15 |
|    4 | rico     | 2014-08-25 14:22:22 |
+------+----------+---------------------+
3 rows in set (0.00 sec)

Que fait cette instruction delete

Pseudocode: regroupez les lignes en deux colonnes dont vous souhaitez supprimer les doublons. Choisissez la ligne de chaque groupe à conserver en utilisant l'agrégat max. Une jointure gauche renvoie toutes les lignes de la table de gauche, avec les lignes correspondantes dans la table de droite. Dans ce cas, la table de gauche contient toutes les lignes de la table et la droite ne contient que les lignes qui sont NULL (pas la ligne que vous souhaitez conserver par groupe). En supprimant ces lignes, il ne vous reste que l’unique par groupe.

Explication plus technique, comment lire cette instruction sql delete:

Les pingouins de table avec l'alias 'a' restent joints sur un sous-ensemble de pingouins de table appelé alias 'b'. Le tableau de droite 'b', qui est un sous-ensemble, trouve l’horodatage maximal groupé par foo et bar. Ceci est adapté à la table de gauche 'a'. (foo, bar, baz) à gauche a chaque ligne du tableau. Le sous-ensemble de droite 'b' a un (maxtimestamp, foo, bar) qui ne correspond à gauche que sur celui qui IS est le max.

Chaque ligne qui n'est pas max a la valeur maxtimestamp de NULL. Filtrez sur ces lignes NULL et vous obtenez un ensemble de toutes les lignes regroupées par foo et bar qui ne correspond pas à la dernière valeur d'horodatage. Supprimer ceux-là. 

Faites une sauvegarde de la table avant de l'exécuter. 

Empêchez ce problème de se reproduire sur cette table:

Si vous obtenez cela au travail, et il éteint votre feu "lignes dupliquées". Génial. Votre travail n'est pas encore terminé. Définissez une nouvelle clé unique composite sur votre table (sur ces deux colonnes) pour éviter que davantage de doublons ne soient ajoutés en premier lieu. Comme un bon système immunitaire, les mauvaises lignes ne devraient même pas être autorisées à entrer dans la table au moment de l'insertion. Plus tard, tous les programmes ajoutant des doublons diffuseront leur protestation, et lorsque vous les corrigez, ce problème ne se pose plus jamais.

45
Eric Leschinski

Cela semble toujours fonctionner pour moi:

CREATE TABLE NoDupeTable LIKE DupeTable; 
INSERT NoDupeTable SELECT * FROM DupeTable group by CommonField1,CommonFieldN;

Qui conserve l'identifiant le plus bas sur chacun des dupes et le reste des enregistrements non-dupes.

J'ai également pris les mesures suivantes pour que le problème de duplication ne se produise plus après la suppression:

CREATE TABLE NoDupeTable LIKE DupeTable; 
Alter table NoDupeTable Add Unique `Unique` (CommonField1,CommonField2);
INSERT IGNORE NoDupeTable SELECT * FROM DupeTable;

En d’autres termes, je crée un duplicata de la première table, j’ajoute un index unique sur les champs pour lesquels je ne veux pas de duplicata, puis fais un Insert IGNORE qui a l’avantage de ne pas échouer comme un Insert normal la première fois qu’il a essayé pour ajouter un enregistrement en double basé sur les deux champs et ignore plutôt de tels enregistrements.

En se déplaçant, il devient impossible de créer des enregistrements en double basés sur ces deux champs.

12
user3649739

Après avoir rencontré ce problème moi-même, sur une base de données volumineuse, je n’ai pas été complètement impressionné par la performance des autres réponses. Je veux garder uniquement la dernière ligne en double et supprimer le reste.

Dans une instruction à requête unique, sans table temporaire, cela fonctionnait mieux pour moi,

DELETE e.*
FROM employee e
WHERE id IN
 (SELECT id
   FROM (SELECT MIN(id) as id
          FROM employee e2
          GROUP BY first_name, last_name
          HAVING COUNT(*) > 1) x);

Le seul inconvénient est que je dois exécuter la requête plusieurs fois, mais même avec cela, j'ai trouvé que cela fonctionnait mieux pour moi que les autres options.

9
seaders

Voici une réponse simple:

delete a from target_table a left JOIN (select max(id_field) as id, field_being_repeated  
    from target_table GROUP BY field_being_repeated) b 
    on a.field_being_repeated = b.field_being_repeated
      and a.id_field = b.id_field
    where b.id_field is null;
7
Ted Celestin

Ce qui suit fonctionne pour toutes les tables

CREATE TABLE `noDup` LIKE `Dup` ;
INSERT `noDup` SELECT DISTINCT * FROM `Dup` ;
DROP TABLE `Dup` ;
ALTER TABLE `noDup` RENAME `Dup` ;
6
M.B.Miri

Ce travail pour moi pour supprimer les anciens enregistrements:

delete from table where id in 
(select min(e.id)
    from (select * from table) e 
    group by column1, column2
    having count(*) > 1
); 

Vous pouvez remplacer min (e.id) à max (e.id) pour supprimer les enregistrements les plus récents.

5
richardhell

Cette procédure supprimera tous les doublons (y compris les multiples) d'un tableau en conservant le dernier doublon. Ceci est une extension de Récupération du dernier enregistrement de chaque groupe

J'espère que cela est utile à quelqu'un.

DROP TABLE IF EXISTS UniqueIDs;
CREATE Temporary table UniqueIDs (id Int(11));

INSERT INTO UniqueIDs
    (SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON
    (T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison Fields 
    AND T1.ID < T2.ID)
    WHERE T2.ID IS NULL);

DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);
4
Simon
delete p from 
product p
inner join (
    select max(id) as id, url from product 
    group by url 
    having count(*) > 1
) unik on unik.url = p.url and unik.id != p.id;
3
temonehm

Un autre moyen facile ... en utilisant UPDATE IGNORE:

Vous devez utiliser un index sur une ou plusieurs colonnes (type index) . Créez une nouvelle colonne de référence temporaire (ne faisant pas partie de l'index). Dans cette colonne, vous marquez les uniques dans en le mettant à jour avec la clause ignore. Pas à pas: 

Ajoutez une colonne de référence temporaire pour marquer les uniques:

ALTER TABLE `yourtable` ADD `unique` VARCHAR(3) NOT NULL AFTER `lastcolname`;

=> cela va ajouter une colonne à votre table. 

Mettez à jour la table, essayez de tout marquer comme unique, mais ignorez les erreurs possibles dues à un problème de clé en double (les enregistrements seront ignorés):

UPDATE IGNORE `yourtable` SET `unique` = 'Yes' WHERE 1;

=> vous constaterez que vos enregistrements en double ne seront pas marqués comme uniques = 'Oui', autrement dit, un seul de chaque ensemble d'enregistrements en double sera marqué comme unique.

Supprimer tout ce qui n'est pas unique:

DELETE * FROM `yourtable` WHERE `unique` <> 'Yes';

=> Ceci supprimera tous les enregistrements en double.

Déposez la colonne ...

ALTER TABLE `yourtable` DROP `unique`;
2
Werner

Je trouve la solution ci-dessus de Werner la plus pratique, car elle fonctionne indépendamment de la présence d'une clé primaire, ne gâche pas les tables, utilise le sql simple et évolutif, est très compréhensible.

Comme je l’ai dit dans mon commentaire, cette solution n’a pas été correctement expliquée… .. C'est donc la mienne, fondée sur celle-ci.

1) ajouter une nouvelle colonne booléenne

alter table mytable add tokeep boolean;

2) ajouter une contrainte sur les colonnes dupliquées ET la nouvelle colonne

alter table mytable add constraint preventdupe unique (mycol1, mycol2, tokeep);

3) définissez la colonne booléenne sur true. Cela ne réussira que sur l'une des lignes dupliquées à cause de la nouvelle contrainte

update ignore mytable set tokeep = true;

4) supprimer les lignes qui n'ont pas été marquées comme étant à conserver

delete from mytable where tokeep is null;

5) déposer la colonne ajoutée

alter table mytable drop tokeep;

Je vous suggère de conserver la contrainte que vous avez ajoutée afin d'éviter de nouveaux doublons à l'avenir.

1
xtian

La suppression des doublons sur les tables MySQL est un problème courant, qui répond généralement à des besoins spécifiques. Au cas où quelqu'un serait intéressé, voici ( Supprimer les lignes en double dans MySQL ) J'explique comment utiliser une table temporaire pour supprimer les doublons MySQL de manière fiable et rapide, également valable pour gérer des sources de données volumineuses cas).

ALi , dans votre cas, vous pouvez exécuter quelque chose comme ceci:

-- create a new temporary table
CREATE TABLE tmp_table1 LIKE table1;

-- add a unique constraint    
ALTER TABLE tmp_table1 ADD UNIQUE(sid, title);

-- scan over the table to insert entries
INSERT IGNORE INTO tmp_table1 SELECT * FROM table1 ORDER BY sid;

-- rename tables
RENAME TABLE table1 TO backup_table1, tmp_table1 TO table1;
1

Cela fonctionne pour les grandes tables:

 CREATE Temporary table duplicates AS select max(id) as id, url from links group by url having count(*) > 1;

 DELETE l from links l inner join duplicates ld on ld.id = l.id WHERE ld.id IS NOT NULL;

Pour supprimer la plus ancienne modification max(id) en min(id)

0
Mugoma J. Okomba

Je pense que cela fonctionnera essentiellement en copiant le tableau et en le vidant, puis en y insérant uniquement les valeurs distinctes, mais vérifiez-le avant de le faire sur de grandes quantités de données.

Crée une copie carbone de votre table

crée une table temp_table comme oldtablename; insert temp_table select * from oldtablename;

Vide ta table d'origine

DELETE * from oldtablename;

Copie toutes les valeurs distinctes de la table copiée dans votre table d'origine

INSERT oldtablename SELECT * du groupe temp_table par prénom, nom, dob

Supprime votre table temporaire.

Drop Table temp_table

Vous devez regrouper par TOUS les champs que vous souhaitez conserver distincts.

0
ChrisAardvark
DELETE T2
FROM   table_name T1
JOIN   same_table_name T2 ON (T1.title = T2.title AND T1.ID <> T2.ID)
0
Nav
delete from `table` where `table`.`SID` in 
    (
    select t.SID from table t join table t1 on t.title = t1.title  where t.SID > t1.SID
)
0
Patrick

J'adore la réponse de @ Eric, mais cela ne semble pas fonctionner si vous avez une très grande table (je reçois The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay lorsque j'essaie de l'exécuter). J'ai donc limité la requête de jointure pour ne prendre en compte que les lignes en double et j'ai fini par:

DELETE a FROM penguins a
    LEFT JOIN (SELECT COUNT(baz) AS num, MIN(baz) AS keepBaz, foo
        FROM penguins
        GROUP BY deviceId HAVING num > 1) b
        ON a.baz != b.keepBaz
        AND a.foo = b.foo
    WHERE b.foo IS NOT NULL

Dans ce cas, la clause WHERE permet à MySQL d’ignorer toute ligne n’ayant pas de doublon et s’il ignore également s’il s’agit de la première instance du doublon, de sorte que seuls les doublons suivants seront ignorés. Remplacez MIN(baz) par MAX(baz) pour conserver la dernière instance au lieu de la première.

0
Gujamin

Ceci fera de la colonne column_name une clé primaire et ignorera toutes les erreurs. Donc, il va supprimer les lignes avec une valeur en double pour column_name.

ALTER IGNORE TABLE `table_name` ADD PRIMARY KEY (`column_name`);