J'ai une table avec les champs suivants:
id (Unique)
url (Unique)
title
company
site_id
Maintenant, je dois supprimer les lignes ayant le même title, company and site_id
. Une façon de le faire consiste à utiliser le code SQL suivant avec un script (PHP
):
SELECT title, site_id, location, id, count( * )
FROM jobs
GROUP BY site_id, company, title, location
HAVING count( * ) >1
Après avoir exécuté cette requête, je peux supprimer les doublons à l'aide d'un script côté serveur.
Mais, je veux savoir si cela peut être fait uniquement en utilisant une requête SQL.
Une méthode très simple consiste à ajouter un index UNIQUE
sur les 3 colonnes. Lorsque vous écrivez l'instruction ALTER
, incluez le mot clé IGNORE
. Ainsi:
ALTER IGNORE TABLE jobs
ADD UNIQUE INDEX idx_name (site_id, title, company);
Cela supprimera toutes les lignes en double. En tant qu'avantage supplémentaire, les futures variables INSERTs
qui se trouvent en double seront susceptibles d'erreur Comme toujours, vous voudrez peut-être faire une sauvegarde avant d'exécuter quelque chose comme ça ...
Si vous ne souhaitez pas modifier les propriétés de la colonne, vous pouvez utiliser la requête ci-dessous.
Puisque vous avez une colonne qui a des identifiants uniques (par exemple, des colonnes auto_increment
), vous pouvez l’utiliser pour supprimer les doublons:
DELETE `a`
FROM
`jobs` AS `a`,
`jobs` AS `b`
WHERE
-- IMPORTANT: Ensures one version remains
-- Change "ID" to your unique column's name
`a`.`ID` < `b`.`ID`
-- Any duplicates you want to check for
AND (`a`.`title` = `b`.`title` OR `a`.`title` IS NULL AND `b`.`title` IS NULL)
AND (`a`.`company` = `b`.`company` OR `a`.`company` IS NULL AND `b`.`company` IS NULL)
AND (`a`.`site_id` = `b`.`site_id` OR `a`.`site_id` IS NULL AND `b`.`site_id` IS NULL);
Dans MySQL, vous pouvez le simplifier encore davantage avec l'opérateur égal NULL-safe (ou "opérateur de vaisseau spatial" ):
DELETE `a`
FROM
`jobs` AS `a`,
`jobs` AS `b`
WHERE
-- IMPORTANT: Ensures one version remains
-- Change "ID" to your unique column's name
`a`.`ID` < `b`.`ID`
-- Any duplicates you want to check for
AND `a`.`title` <=> `b`.`title`
AND `a`.`company` <=> `b`.`company`
AND `a`.`site_id` <=> `b`.`site_id`;
MySQL a des restrictions quant à la référence à la table que vous supprimez. Vous pouvez contourner cela avec une table temporaire, comme:
create temporary table tmpTable (id int);
insert tmpTable
(id)
select id
from YourTable yt
where exists
(
select *
from YourTabe yt2
where yt2.title = yt.title
and yt2.company = yt.company
and yt2.site_id = yt.site_id
and yt2.id > yt.id
);
delete
from YourTable
where ID in (select id from tmpTable);
De la suggestion de Kostanos dans les commentaires:
La seule requête lente ci-dessus est DELETE, dans les cas où vous avez une très grande base de données. Cette requête pourrait être plus rapide:
DELETE FROM YourTable USING YourTable, tmpTable WHERE YourTable.id=tmpTable.id
Si l'instruction IGNORE
ne fonctionne pas comme dans mon cas, vous pouvez utiliser l'instruction ci-dessous:
CREATE TABLE your_table_deduped like your_table;
INSERT your_table_deduped SELECT * FROM your_table GROUP BY index1_id, index2_id;
RENAME TABLE your_table TO your_table_with_dupes;
RENAME TABLE your_table_deduped TO your_table;
#OPTIONAL
ALTER TABLE `your_table` ADD UNIQUE `unique_index` (`index1_id`, `index2_id`);
#OPTIONAL
DROP TABLE your_table_with_dupes;
Il y a une autre solution:
DELETE t1 FROM my_table t1, my_table t2 WHERE t1.id < t2.id AND t1.my_field = t2.my_field AND t1.my_field_2 = t2.my_field_2 AND ...
La suppression de doublons sur les tables MySQL est un problème courant. Elle résulte généralement d’une contrainte manquante qui permet d’éviter ces doublons au préalable. Mais ce problème commun s'accompagne généralement de besoins spécifiques… qui nécessitent des approches spécifiques. L’approche doit être différente en fonction, par exemple, de la taille des données, de l’entrée dupliquée à conserver (en général la première ou la dernière), de la possibilité de conserver des index ou de la nécessité d’effectuer des opérations supplémentaires. action sur les données dupliquées.
Il y a aussi quelques spécificités sur MySQL lui-même, comme l'impossibilité de faire référence à la même table sur une cause FROM lors de l'exécution d'une table UPDATE (cela provoquera l'erreur MySQL # 1093). Cette limitation peut être surmontée en utilisant une requête interne avec une table temporaire (comme suggéré dans certaines approches ci-dessus). Mais cette requête interne ne fonctionnera pas particulièrement bien avec les sources de données volumineuses.
Cependant, une meilleure approche existe pour supprimer les doublons, à la fois efficace et fiable, et pouvant être facilement adaptée à différents besoins.
L'idée générale est de créer une nouvelle table temporaire, en ajoutant généralement une contrainte unique pour éviter les doublons, et d'insérer les données de votre ancienne table dans la nouvelle, tout en prenant soin des doublons. Cette approche repose sur de simples requêtes MySQL INSERT, crée une nouvelle contrainte pour éviter les doublons et évite la nécessité d'utiliser une requête interne pour rechercher des doublons et une table temporaire devant être conservée en mémoire (ajustant ainsi les sources de données volumineuses).
Voici comment cela peut être réalisé. Étant donné que nous avons une table employee, avec les colonnes suivantes:
employee (id, first_name, last_name, start_date, ssn)
Afin de supprimer les lignes avec une colonne dupliquée ssn et de ne conserver que la première entrée trouvée, le processus suivant peut être suivi:
-- create a new tmp_eployee table
CREATE TABLE tmp_employee LIKE employee;
-- add a unique constraint
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
-- scan over the employee table to insert employee entries
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id;
-- rename tables
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
⇒ _ {En utilisant cette approche, 1,6 million de registres ont été convertis en 6k en moins de 200s.}
Chetan , en suivant ce processus, vous pouvez rapidement et facilement supprimer tous vos doublons et créer une contrainte UNIQUE en exécutant:
CREATE TABLE tmp_jobs LIKE jobs;
ALTER TABLE tmp_jobs ADD UNIQUE(site_id, title, company);
INSERT IGNORE INTO tmp_jobs SELECT * FROM jobs ORDER BY id;
RENAME TABLE jobs TO backup_jobs, tmp_jobs TO jobs;
Bien sûr, ce processus peut être encore modifié pour l'adapter à différents besoins lors de la suppression de doublons. Quelques exemples suivent.
Parfois, nous devons conserver la dernière entrée dupliquée au lieu de la première.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id DESC;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
Parfois, nous devons effectuer un traitement supplémentaire sur les entrées dupliquées trouvées (telles que la gestion du nombre de doublons).
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
Parfois, nous utilisons un champ auto-incrémental et, afin de garder l'index aussi compact que possible, nous pouvons tirer parti de la suppression des doublons pour régénérer le champ auto-incrémental de la nouvelle table temporaire.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT (first_name, last_name, start_date, ssn) FROM employee ORDER BY id;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
De nombreuses autres modifications sont également réalisables en fonction du comportement souhaité. Par exemple, les requêtes suivantes utiliseront une seconde table temporaire pour, en plus de 1) conserver la dernière entrée au lieu de la première; et 2) augmenter un compteur sur les doublons trouvés; également 3) régénérer l'identifiant de champ auto-incrémental tout en conservant l'ordre de saisie tel qu'il était sur les données précédentes.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id DESC ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
CREATE TABLE tmp_employee2 LIKE tmp_employee;
INSERT INTO tmp_employee2 SELECT (first_name, last_name, start_date, ssn) FROM tmp_employee ORDER BY id;
DROP TABLE tmp_employee;
RENAME TABLE employee TO backup_employee, tmp_employee2 TO employee;
J'ai ce snipet de requête pour SQLServer mais je pense qu'il peut être utilisé dans d'autres SGBD avec de petites modifications:
DELETE
FROM Table
WHERE Table.idTable IN (
SELECT MAX(idTable)
FROM idTable
GROUP BY field1, field2, field3
HAVING COUNT(*) > 1)
J'ai oublié de vous dire que cette requête ne supprime pas la ligne avec l'id le plus bas des lignes dupliquées. Si cela fonctionne pour vous essayez cette requête:
DELETE
FROM jobs
WHERE jobs.id IN (
SELECT MAX(id)
FROM jobs
GROUP BY site_id, company, title, location
HAVING COUNT(*) > 1)
Simple et rapide pour tous les cas:
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_duplicates AS (SELECT dub.id FROM table_with_duplications dub GROUP BY dub.field_must_be_uniq_1, dub.field_must_be_uniq_2 HAVING COUNT(*) > 1);
DELETE FROM table_with_duplications WHERE id IN (SELECT id FROM _temp_duplicates);
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;
TRUNCATE TABLE tableName;
INSERT INTO tableName SELECT * FROM tempTableName;
DROP TABLE tempTableName;
Je continue à visiter cette page chaque fois que je recherche "supprimer les doublons de MySQL", mais les solutions theIGNORE ne fonctionnent pas car je dispose de tables InnoDB MySQL
ce code fonctionne mieux à tout moment
CREATE TABLE tableToclean_temp LIKE tableToclean;
ALTER TABLE tableToclean_temp ADD UNIQUE INDEX (fontsinuse_id);
INSERT IGNORE INTO tableToclean_temp SELECT * FROM tableToclean;
DROP TABLE tableToclean;
RENAME TABLE tableToclean_temp TO tableToclean;
tableToclean = le nom de la table à nettoyer
tableToclean_temp = une table temporaire créée et supprimée
Une solution simple à comprendre et qui fonctionne sans clé primaire:
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.
Cette solution déplacera les doublons dans une table et les uniques dans une autre.
-- speed up creating uniques table if dealing with many rows
CREATE INDEX temp_idx ON jobs(site_id, company, title, location);
-- create the table with unique rows
INSERT jobs_uniques SELECT * FROM
(
SELECT *
FROM jobs
GROUP BY site_id, company, title, location
HAVING count(1) > 1
UNION
SELECT *
FROM jobs
GROUP BY site_id, company, title, location
HAVING count(1) = 1
) x
-- create the table with duplicate rows
INSERT jobs_dupes
SELECT *
FROM jobs
WHERE id NOT IN
(SELECT id FROM jobs_uniques)
-- confirm the difference between uniques and dupes tables
SELECT COUNT(1)
AS jobs,
(SELECT COUNT(1) FROM jobs_dupes) + (SELECT COUNT(1) FROM jobs_uniques)
AS sum
FROM jobs
si vous avez une grande table avec un très grand nombre d'enregistrements, les solutions ci-dessus ne fonctionneront pas ou prendront trop de temps. Ensuite, nous avons une solution différente
-- Create temporary table
CREATE TABLE temp_table LIKE table1;
-- Add constraint
ALTER TABLE temp_table ADD UNIQUE(title, company,site_id);
-- Copy data
INSERT IGNORE INTO temp_table SELECT * FROM table1;
-- Rename and drop
RENAME TABLE table1 TO old_table1, temp_table TO table1;
DROP TABLE old_table1;
Supprimer les lignes en double à l’aide de l’instruction DELETE JOIN MySQL vous fournit l’instruction DELETE JOIN que vous pouvez utiliser pour supprimer rapidement les lignes en double.
L'instruction suivante supprime les lignes en double et conserve l'identifiant le plus élevé:
DELETE t1 FROM contacts t1
INNER JOIN
contacts t2 WHERE
t1.id < t2.id AND t1.email = t2.email;
J'ai trouvé un moyen simple. (garder au plus tard)
DELETE t1 FROM tablename t1 INNER JOIN tablename t2
WHERE t1.id < t2.id AND t1.column1 = t2.column1 AND t1.column2 = t2.column2;
Depuis la version 8.0 (2018), MySQL supporte enfin les fonctions window .
Les fonctions de la fenêtre sont à la fois pratiques et efficaces. Voici une solution qui montre comment les utiliser pour résoudre cette tâche.
Dans une sous-requête, nous pouvons utiliser ROW_NUMBER()
pour attribuer une position à chaque enregistrement de la table au sein de column1/column2
groupes, classés par id
. S'il n'y a pas de doublons, l'enregistrement recevra le numéro de ligne 1
. Si des doublons existent, ils seront numérotés par ordre croissant id
(à partir de 1
).
Une fois que les enregistrements sont correctement numérotés dans la sous-requête, la requête externe supprime simplement tous les enregistrements dont le numéro de ligne n'est pas 1.
Requête:
DELETE FROM tablename
WHERE id IN (
SELECT id
FROM (
SELECT
id,
ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id) rn
FROM output
) t
WHERE rn > 1
)
Pour dupliquer des enregistrements avec des colonnes uniques, p. Ex. COL1, COL2, COL3 ne doivent pas être répliqués (supposons que nous ayons oublié 3 colonnes uniques dans la structure de la table et que plusieurs entrées en double aient été créées dans la table)
DROP TABLE TABLE_NAME_copy;
CREATE TABLE TABLE_NAME_copy LIKE TABLE_NAME;
INSERT INTO TABLE_NAME_copy
SELECT * FROM TABLE_NAME
GROUP BY COLUMN1, COLUMN2, COLUMN3;
DROP TABLE TABLE_NAME;
ALTER TABLE TABLE_NAME_copy RENAME TO TABLE_NAME;
Espoir aidera dev.
Un tutoriel très décrit pour résoudre ce problème peut être trouvé sur mysqltutorial.org site:
Comment supprimer les lignes en double dans MySQL
Il est très clairement montré comment supprimer les lignes en double de trois manières différentes :
A) Utilisation de l'instruction DELETE JOIN
B) Utilisation d'une table intermédiaire
C) Utilisation de la fonction ROW_NUMBER()
J'espère que ça va aider quelqu'un.
Supprimer l'enregistrement en double dans une table.
delete from job s
where rowid < any
(select rowid from job k
where s.site_id = k.site_id and
s.title = k.title and
s.company = k.company);
ou
delete from job s
where rowid not in
(select max(rowid) from job k
where s.site_id = k.site_id and
s.title = k.title and
s.company = k.company);
-- Here is what I used, and it works:
create table temp_table like my_table;
-- t_id is my unique column
insert into temp_table (id) select id from my_table GROUP by t_id;
delete from my_table where id not in (select id from temp_table);
drop table temp_table;