J'ai besoin de DELETE
lignes dupliquées pour sid spécifié sur unMySQL
table.
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.
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
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
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
)
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.
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.
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.
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;
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` ;
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.
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);
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;
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`;
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.
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;
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)
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.
DELETE T2
FROM table_name T1
JOIN same_table_name T2 ON (T1.title = T2.title AND T1.ID <> T2.ID)
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
)
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.
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`);