J'ai besoin de DELETE
lignes dupliquées pour le Sid spécifié sur une MySQL
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 faire.
J'ai besoin de DELETE
lignes dupliquées pour le Sid spécifié sur une MySQL
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 faire.
Réponses:
cela supprime les doublons en place, sans créer de nouvelle table
ALTER IGNORE TABLE `table_name` ADD UNIQUE (title, SID)
note: ne fonctionne bien que si l'index tient en mémoire
ALTER IGNORE
.
ALTER TABLE foo ENGINE MyISAM
pour contourner le problème, j'ai changé le moteur après.
Supposons que vous ayez une table employee
, avec les colonnes suivantes:
employee (first_name, last_name, start_date)
Pour supprimer les lignes avec une first_name
colonne en double :
delete
from employee using employee,
employee e1
where employee.id > e1.id
and employee.first_name = e1.first_name
employee
contre elle-même pour une correspondance d'index et une >
vérification sur un index va être lente pour les grandes tables. Ne serait-il pas préférable de SELECT MAX(ID) FROM t GROUP BY unique
et ensuite JOIN
une correspondance exacte de ID
à MAX(ID)
?
Ensuite, supprimez les doublons pour tous les SID, pas seulement pour un seul.
Avec table de température
CREATE TABLE table_temp AS
SELECT * FROM table GROUP BY title, SID;
DROP TABLE table;
RENAME TABLE table_temp TO table;
Depuis sa temp_table
création récente, il n'a pas d'index. Vous devrez les recréer après avoir supprimé les doublons. Vous pouvez vérifier les index que vous avez dans le tableau avecSHOW INDEXES IN table
Sans table de température:
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
)
SELECT * FROM table GROUP BY title, SID;
Tout dépend de la façon dont vous savez ce que vous faites.
Créez le tableau et insérez quelques lignes:
create table penguins(foo int, bar varchar(15), baz datetime);
insert into penguins values(1, 'skipper', now());
insert into penguins values(1, 'skipper', now());
insert into penguins values(3, 'kowalski', now());
insert into penguins values(3, 'kowalski', now());
insert into penguins values(3, 'kowalski', now());
insert into penguins values(4, 'rico', now());
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)
Supprimez les doublons en place:
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)
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)
Vous avez terminé, les lignes en double sont supprimées, la dernière par horodatage est conservée.
Vous n'avez pas de timestamp
colonne d'index ou une colonne d'index unique pour trier? Vous vivez dans un état de dégénérescence. Vous devrez effectuer des étapes supplémentaires pour supprimer les lignes en double.
créer la table des pingouins et ajouter quelques lignes
create table penguins(foo int, bar varchar(15));
insert into penguins values(1, 'skipper');
insert into penguins values(1, 'skipper');
insert into penguins values(3, 'kowalski');
insert into penguins values(3, 'kowalski');
insert into penguins values(3, 'kowalski');
insert into penguins values(4, 'rico');
select * from penguins;
# +------+----------+
# | foo | bar |
# +------+----------+
# | 1 | skipper |
# | 1 | skipper |
# | 3 | kowalski |
# | 3 | kowalski |
# | 3 | kowalski |
# | 4 | rico |
# +------+----------+
faites un clone de la première table et copiez-y.
drop table if exists penguins_copy;
create table penguins_copy as ( SELECT foo, bar FROM penguins );
#add an autoincrementing primary key:
ALTER TABLE penguins_copy ADD moo int AUTO_INCREMENT PRIMARY KEY first;
select * from penguins_copy;
# +-----+------+----------+
# | moo | foo | bar |
# +-----+------+----------+
# | 1 | 1 | skipper |
# | 2 | 1 | skipper |
# | 3 | 3 | kowalski |
# | 4 | 3 | kowalski |
# | 5 | 3 | kowalski |
# | 6 | 4 | rico |
# +-----+------+----------+
L'agrégat max fonctionne sur le nouvel index moo:
delete a from penguins_copy a left join(
select max(moo) myindex, foo, bar
from penguins_copy
group by foo, bar) b
on a.moo = b.myindex and
a.foo = b.foo and
a.bar = b.bar
where b.myindex IS NULL;
#drop the extra column on the copied table
alter table penguins_copy drop moo;
select * from penguins_copy;
#drop the first table and put the copy table back:
drop table penguins;
create table penguins select * from penguins_copy;
observer et nettoyer
drop table penguins_copy;
select * from penguins;
+------+----------+
| foo | bar |
+------+----------+
| 1 | skipper |
| 3 | kowalski |
| 4 | rico |
+------+----------+
Elapsed: 1458.359 milliseconds
Que fait cette grosse instruction de suppression SQL?
Les pingouins de table avec l'alias «a» sont laissés joints sur un sous-ensemble de pingouins de table appelé alias «b». La table de droite 'b' qui est un sous-ensemble trouve l'horodatage max [ou max moo] groupé par les colonnes foo et bar. Cela correspond au tableau de gauche «a». (foo, bar, baz) sur la gauche a toutes les lignes du tableau. Le sous-ensemble de droite 'b' a un (maxtimestamp, foo, bar) qui correspond à gauche uniquement sur celui qui EST le max.
Chaque ligne qui n'est pas ce max a pour valeur maxtimestamp NULL. Filtrez vers le bas sur ces lignes NULL et vous avez un ensemble de toutes les lignes regroupées par foo et bar qui ne sont pas le dernier horodatage baz. Supprimez-les.
Faites une sauvegarde de la table avant de l'exécuter.
Empêchez ce problème de se reproduire sur cette table:
Si cela fonctionne, et que cela éteint votre feu de "ligne en double". Génial. Définissez maintenant une nouvelle clé unique composite sur votre table (sur ces deux colonnes) pour éviter que d'autres 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 sur la table au moment de l'insertion. Plus tard, tous ces programmes ajoutant des doublons diffuseront leur protestation, et lorsque vous les corrigerez, ce problème ne se reproduira plus.
ID
colonne à incrémentation automatique , la ON
clause doit uniquement correspondre à la ID
colonne, rien d'autre.
Après avoir rencontré ce problème moi-même, sur une énorme base de données, je n'ai pas été complètement impressionné par la performance de l'une des autres réponses. Je souhaite ne conserver que la dernière ligne en double et supprimer le reste.
Dans une instruction à une seule requête, sans table temporaire, cela a fonctionné 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);
La seule mise en garde 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.
Cela semble toujours fonctionner pour moi:
CREATE TABLE NoDupeTable LIKE DupeTable;
INSERT NoDupeTable SELECT * FROM DupeTable group by CommonField1,CommonFieldN;
Ce qui conserve l'ID le plus bas sur chacune des dupes et le reste des enregistrements non-dupes.
J'ai également pris les mesures suivantes pour que le problème de dupe 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 dont je ne veux pas de duplicata, puis j'en fais un Insert IGNORE
qui a l'avantage de ne pas échouer comme d'habitude le Insert
ferait la première fois qu'il essaie d'ajouter un enregistrement en double basé sur les deux champs et ignore plutôt ces enregistrements.
En se déplaçant vers l'avant, il devient impossible de créer des enregistrements en double basés sur ces deux champs.
ORDER BY
dans le SELECT
pour être sûr de quel disque est réellement passé au NoDupeTable
?
ORDER by ID Asc
ne peut pas nuire , donc je vais modifier ma nontheless de réponse.
Select Max(ID)
et ensuite, Order by Max(ID)
mais tout ce que cela ferait est d'inverser l'ordre de l'insertion. Pour saisir l'ID le plus élevé, il faudrait, je crois, une jointure de sélection plus complexe car, quelle que soit la façon dont vous commandez ci-dessus, vous récupérerez les valeurs de champ de l'ID inférieur.
MAX(ID)
ou MIN(ID)
et au lieu de *
dans le SELECT FROM DupeTable
cependant, sinon vous obtiendrez simplement l'un des noms au ID
hasard. En fait, de nombreux SQL et même MySQL strict nécessitent l'appel d'une fonction d'agrégation sur chaque colonne non spécifiée dans la GROUP BY
clause.
ID,First,Last,Notes
et des enregistrements 1,Bob,Smith,NULL
, 2,Bob,Smith,Arrears
puis que SELECT *Max(ID), First,Last,Notes FROM DupeTable group by First,Last
je faisais un , les deux renverraient le même enregistrement, 1, sauf avec un ID différent. Max (ID) reviendrait 2,Bob,Smith,NULL
et Min (ID) reviendrait 1,Bob,Smith,NULL
. Pour obtenir le deuxième enregistrement avec «Arriérés» dans les notes, il faut une jointure, je crois.
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;
and a.id_field = b.id
LEFT JOIN
à b
n'a besoin que de comparer b.id
= en a.id_field
supposant qu'il field_id
s'agit d'un ID d'incrémentation automatique unique. a.field_being_repeated = b.field_being_repeated
est donc étranger. ( b.id_field
n'existe pas non plus dans cette requête, c'est b.id
.
Ce travail pour moi de 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) par max (e.id) pour supprimer les enregistrements les plus récents.
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;
Je trouve que la solution de Werner ci-dessus est la plus pratique car elle fonctionne quelle que soit la présence d'une clé primaire, ne joue pas avec les tables, utilise un SQL simple à l'épreuve du temps, est très compréhensible.
Comme je l'ai dit dans mon commentaire, cette solution n'a cependant pas été correctement expliquée. C'est donc à moi, basé sur cela.
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 en raison de la nouvelle contrainte
update ignore mytable set tokeep = true;
4) supprimer les lignes qui n'ont pas été marquées comme à conserver
delete from mytable where tokeep is null;
5) Déposez 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.
Cette procédure supprimera tous les doublons (y compris les multiples) dans une table, en conservant le dernier duplicata. Ceci est une extension de Récupération du dernier enregistrement dans 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);
Un autre moyen simple ... 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 en la 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 ajoutera 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 verrez que vos enregistrements en double ne seront pas marqués comme uniques = 'Oui', en d'autres termes, un seul de chaque ensemble d'enregistrements en double sera marqué comme unique.
Supprimez tout ce qui n'est pas unique:
DELETE * FROM `yourtable` WHERE `unique` <> 'Yes';
=> Cela supprimera tous les enregistrements en double.
Déposez la colonne ...
ALTER TABLE `yourtable` DROP `unique`;
unique
colonne DOIT être ajoutée à une contrainte unique avec les colonnes qui sont actuellement dupliquées, sinon tout ne fonctionne pas car SET unique
= 'Yes' n'échouerait jamais.
unique
s'agit d'un mot clé mysql. Il doit donc avoir les backticks (comme déjà correctement affichés). Utiliser un autre mot pour la colonne peut être plus pratique.
La suppression des doublons sur les tables MySQL est un problème courant, qui vient généralement avec des besoins spécifiques. Au cas où quelqu'un serait intéressé, ici ( Supprimer les lignes en double dans MySQL ) J'explique comment utiliser une table temporaire pour supprimer les doublons de MySQL de manière fiable et rapide, également valable pour gérer les sources de données volumineuses (avec des exemples pour différents cas d'utilisation).
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;
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
quand j'essaye de l'exécuter). J'ai donc limité la requête de jointure pour ne considérer que les lignes en double et je me suis retrouvé avec:
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
La clause WHERE dans ce cas permet à MySQL d'ignorer toute ligne qui n'a pas de doublon et ignorera également s'il s'agit de la première instance du doublon, donc 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.
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 modification la plus ancienne max(id)
demin(id)
Ceci transformera la colonne column_name
en clé primaire et ignorera en attendant toutes les erreurs. Ainsi, il supprimera les lignes avec une valeur en double pour column_name
.
ALTER IGNORE TABLE `table_name` ADD PRIMARY KEY (`column_name`);
Je pense que cela fonctionnera essentiellement en copiant la table et en la vidant, puis en n'y remettant que les valeurs distinctes, mais veuillez le vérifier avant de le faire sur de grandes quantités de données.
Crée une copie carbone de votre table
créer une table temp_table comme oldtablename; insert temp_table select * from oldtablename;
Vide votre table d'origine
DELETE * de l'ancien nom de la table;
Copie toutes les valeurs distinctes de la table copiée dans votre table d'origine
INSÉRER oldtablename SELECT * du groupe temp_table par prénom, nom, dob
Supprime votre table temporaire.
Drop Table temp_table
Vous devez regrouper 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)
voici comment j'élimine habituellement les doublons
Vous pouvez simplement utiliser une clause DISTINCT pour sélectionner la liste "nettoyée" (et voici un exemple très simple sur la façon de procéder).
DISTINCT
vous, vous perdez toutes les informations sur les doublons que vous auriez pu avoir en premier lieu. Pouvez-vous montrer un moyen de supprimer les doublons en l'utilisant?
Cela pourrait-il fonctionner si vous les comptez, puis ajoutez une limite à votre requête de suppression en n'en laissant qu'une?
Par exemple, si vous en avez deux ou plus, écrivez votre requête comme ceci:
DELETE FROM table WHERE SID = 1 LIMIT 1;
Il n'y a que quelques étapes de base pour supprimer les données en double de votre table:
Voici le tutoriel complet: https://blog.teamsql.io/deleting-duplicate-data-3541485b3473