web-dev-qa-db-fra.com

Comment supprimer les entrées en double d'une base de données mysql?

J'ai une table avec quelques identifiants + titres. Je veux rendre la colonne de titre unique, mais elle contient déjà plus de 600 000 enregistrements, dont certains sont des doublons (parfois plusieurs dizaines de fois).

Comment puis-je supprimer tous les doublons, sauf un, afin de pouvoir ajouter une clé UNIQUE à la colonne de titre après?

37
user15063

Cette commande ajoute une clé unique et supprime toutes les lignes générant des erreurs (en raison de la clé unique). Cela supprime les doublons.

ALTER IGNORE TABLE table ADD UNIQUE KEY idx1(title); 

Edit: Notez que cette commande peut ne pas fonctionner pour les tables InnoDB pour certaines versions de MySQL. Voir cet article pour une solution de contournement. (Merci à "un utilisateur anonyme" pour cette information.)

79
unutbu

Créez une nouvelle table avec uniquement les lignes distinctes de la table d'origine. Il y a peut-être d'autres moyens mais je trouve que c'est le plus propre.

CREATE TABLE tmp_table AS SELECT DISTINCT [....] FROM main_table

Plus précisement :
Le moyen le plus rapide consiste à insérer des lignes distinctes dans une table temporaire. En utilisant delete, il m'a fallu quelques heures pour supprimer les doublons d'un tableau de 8 millions de lignes. En utilisant insert et distinct, cela n'a pris que 13 minutes. 

CREATE TABLE tempTableName LIKE tableName;  
CREATE INDEX ix_all_id ON tableName(cellId,attributeId,entityRowId,value);  
INSERT INTO tempTableName(cellId,attributeId,entityRowId,value) SELECT DISTINCT cellId,attributeId,entityRowId,value FROM tableName;  
DROP TABLE tableName;  
INSERT tableName SELECT * FROM tempTableName;  
DROP TABLE tempTableName;  
8
nc3b

La suppression des doublons sur les tables MySQL est un problème courant, qui répond généralement à des besoins spécifiques. Si quelqu'un est intéressé, cliquez ici ( Supprimer les lignes en double dans MySQL ) pour expliquer comment utiliser une table temporaire pour supprimer les doublons MySQL de manière fiable et rapide (avec des exemples pour différents cas d'utilisation).

Dans ce cas, quelque chose comme ceci devrait fonctionner:

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

-- add a unique constraint    
ALTER TABLE tmp_table1 ADD UNIQUE(id, 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;
0

Depuis que MySql ALTER IGNORE TABLEest déconseillé , vous devez supprimer la date de duplication avant d’ajouter un index.

Commencez par écrire une requête qui trouve tous les doublons. Je suppose ici que email est le champ qui contient des doublons.

SELECT
    s1.email
    s1.id, 
    s1.created
    s2.id,
    s2.created 
FROM 
    student AS s1 
INNER JOIN 
    student AS s2 
WHERE 
    /* Emails are the same */
    s1.email = s2.email AND
    /* DON'T select both accounts,
       only select the one created later.
       The serial id could also be used here */
    s2.created > s1.created 
;

Ensuite, sélectionnez uniquement les identifiants en double uniques:

SELECT 
    DISTINCT s2.id
FROM 
    student AS s1 
INNER JOIN 
    student AS s2 
WHERE 
    s1.email = s2.email AND
    s2.created > s1.created 
;

Une fois que vous êtes sûr que ne contient que les identifiants en double que vous souhaitez supprimer, exécutez la suppression. Vous devez ajouter (SELECT * FROM tblname) pour que MySql ne se plaint pas.

DELETE FROM
    student 
WHERE
    id
IN (
    SELECT 
        DISTINCT s2.id
    FROM 
        (SELECT * FROM student) AS s1 
    INNER JOIN 
        (SELECT * FROM student) AS s2 
    WHERE 
        s1.email = s2.email AND
        s2.created > s1.created 
);

Créez ensuite l'index unique:

ALTER TABLE
    student
ADD UNIQUE INDEX
    idx_student_unique_email(email)
;
0

Cela montre comment procéder dans SQL2000. Je ne connais pas parfaitement la syntaxe MySQL, mais je suis sûr qu'il y a quelque chose de comparable.

create table #titles (iid int identity (1, 1), title varchar(200))

-- Repeat this step many times to create duplicates
insert into #titles(title) values ('bob')
insert into #titles(title) values ('bob1')
insert into #titles(title) values ('bob2')
insert into #titles(title) values ('bob3')
insert into #titles(title) values ('bob4')


DELETE T  FROM 
#titles T left join 
(
  select title, min(iid) as minid from #titles group by title
) D on T.title = D.title and T.iid = D.minid
WHERE D.minid is null

Select * FROM #titles
0
souLTower
delete from student where id in (
SELECT distinct(s1.`student_id`) from student as s1 inner join student as s2
where s1.`sex` = s2.`sex` and
s1.`student_id` > s2.`student_id` and
s1.`sex` = 'M'
    ORDER BY `s1`.`student_id` ASC
)
0
Nitin

La requête ci-dessous peut être utilisée pour supprimer tous les doublons sauf la ligne avec la plus petite valeur de champ "id" 

DELETE t1 FROM table_name t1, table_name t2 WHERE t1.id > t2.id AND t1.name = t2.name

De la même manière, nous pouvons conserver la ligne avec la valeur la plus élevée dans 'id' comme suit

 DELETE t1 FROM table_name t1, table_name t2 WHERE t1.id < t2.id AND t1.name = t2.name
0

La solution publiée par Nitin semble être la plus élégante/logique.

Cependant, il a un problème:

ERREUR 1093 (HY000): Vous ne pouvez pas spécifier de table cible 'étudiant' pour mise à jour dans la clause FROM

Cela peut toutefois être résolu en utilisant (SELECT * FROM student) au lieu de student:

DELETE FROM student WHERE id IN (
SELECT distinct(s1.`student_id`) FROM (SELECT * FROM student) AS s1 INNER JOIN (SELECT * FROM student) AS s2
WHERE s1.`sex` = s2.`sex` AND
s1.`student_id` > s2.`student_id` AND
s1.`sex` = 'M'
ORDER BY `s1`.`student_id` ASC
)

Donnez vos +1 à Nitin pour avoir proposé la solution originale.

0
StéphanS