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?
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.)
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;
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;
Depuis que MySql ALTER IGNORE TABLE
est 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)
;
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
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
)
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
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.