Question

J'ai de grosses bases de données MySQL (> Mil rows) gâchées par des doublons. Je pense que cela pourrait être 1/4 à la moitié de la base de données remplie avec eux. Je dois me débarrasser d'eux rapidement (je parle du temps d'exécution de la requête). Voici à quoi ça ressemble:
id (index) | text1 | text2 | text3
text1 & amp; la combinaison text2 doit être unique, s'il existe des doublons, une seule combinaison avec text3 NOT NULL doit rester. Exemple:

1 | abc | def | NULL  
2 | abc | def | ghi  
3 | abc | def | jkl  
4 | aaa | bbb | NULL  
5 | aaa | bbb | NULL  

... devient:

1 | abc | def | ghi   #(doesn't realy matter id:2 or id:3 survives)   
2 | aaa | bbb | NULL  #(if there's no NOT NULL text3, NULL will do)

Les nouveaux identifiants ne sont peut-être rien, ils ne dépendent pas des anciens identifiants de table.
J'ai essayé des choses comme:

CREATE TABLE tmp SELECT text1, text2, text3
FROM my_tbl;
GROUP BY text1, text2;
DROP TABLE my_tbl;
ALTER TABLE tmp RENAME TO my_tbl;

Ou SELECT DISTINCT et d’autres variantes.
Bien qu’ils travaillent sur de petites bases de données, le temps d’exécution des requêtes sur le mien est énorme (jamais jusqu’à la fin, en réalité; > 20 min)

Existe-t-il un moyen plus rapide de le faire? S'il vous plaît, aidez-moi à résoudre ce problème.

Était-ce utile?

La solution

Je crois que cela se fera, en utilisant la clé dupliquée + ifnull ():

create table tmp like yourtable;

alter table tmp add unique (text1, text2);

insert into tmp select * from yourtable 
    on duplicate key update text3=ifnull(text3, values(text3));

rename table yourtable to deleteme, tmp to yourtable;

drop table deleteme;

Devrait être beaucoup plus rapide que tout ce qui nécessite un groupe par ou distinct, une sous-requête ou même un ordre par. Cela n’a même pas besoin d’un portage de fichiers, ce qui réduira les performances d’une grande table temporaire. Il faudra quand même effectuer une analyse complète de la table d'origine, mais rien ne peut l'éviter.

Autres conseils

Ce code simple d'une ligne a été trouvé pour faire exactement ce dont j'avais besoin:

ALTER IGNORE TABLE dupTest ADD UNIQUE INDEX(a,b);

Extrait de: http://mediakey.dk/~cc/mysql-remove-duplicate-entries/

DELETE FROM dups
WHERE id NOT IN(
    SELECT id FROM (
        SELECT DISTINCT id, text1, text2
            FROM dups
        GROUP BY text1, text2
        ORDER BY text3 DESC
    ) as tmp
)

Ceci interroge tous les enregistrements, les groupes selon les champs de distinction et les ordres par ID (signifie que nous sélectionnons le premier enregistrement text3 non nul). Ensuite, nous sélectionnons les identifiants de ce résultat (ce sont de bons identifiants ... ils ne seront pas supprimés) et supprimons tous les identifiants qui ne sont pas ces identifiants.

Toute requête de ce type concernant l'ensemble de la table sera lente. Vous avez juste besoin de l'exécuter et de le laisser se dérouler pour pouvoir le prévenir à l'avenir.

Après avoir effectué cette opération, "réparer". J'appliquerais UNIQUE INDEX (text1, text2) à cette table. Prévenir la possibilité de doublons à l'avenir.

Si vous souhaitez utiliser le " créer une nouvelle table et remplacer l’ancienne " route. Vous pouvez utiliser l’instruction select très interne pour créer votre instruction insert.

Spécifique à MySQL (en supposant que la nouvelle table s'appelle my_tbl2 et a exactement la même structure):

INSERT INTO my_tbl2
    SELECT DISTINCT id, text1, text2, text3
            FROM dups
        GROUP BY text1, text2
        ORDER BY text3 DESC

Voir Insertion MySQL ... SELECT pour plus d'informations.

supprimer les doublons sans supprimer les clés étrangères

create table tmp like mytable;
ALTER TABLE tmp ADD UNIQUE INDEX(text1, text2, text3, text4, text5, text6);
insert IGNORE into tmp select * from mytable;
delete from mytable where id not in ( select id from tmp);

Si vous pouvez créer une nouvelle table, utilisez une clé unique dans les champs text1 + text2. Ensuite, insérez dans la table en ignorant les erreurs (en utilisant la syntaxe INSERT IGNORE):

select * from my_tbl order by text3 desc
  • Je pense que l'ordre de text3 desc mettra les valeurs NULL en dernier lieu, mais vérifiez bien cela.

Les index sur toutes ces colonnes pourraient aider beaucoup, mais les créer maintenant pourrait être assez lent.

Pour les grandes tables avec peu de doublons, vous pouvez éviter de copier la table entière à un autre endroit. Une solution consiste à créer une table temporaire contenant les lignes que vous souhaitez conserver (pour chaque clé avec des doublons), puis à supprimer les doublons de la table d'origine.

Un exemple est donné ici .

Je n'ai pas beaucoup d'expérience avec MySQL. S'il possède des fonctions analytiques, essayez:

delete from my_tbl
 where id in (
     select id 
       from (select id, row_number()
                            over (partition by text1, text2 order by text3 desc) as rn
               from my_tbl
               /* optional: where text1 like 'a%'  */
             ) as t2
       where rn > 1
     )

La clause optionnelle Where permet de l'exécuter plusieurs fois, une par lettre, etc. Créez un index sur text1?

Avant de l'exécuter, confirmez que & text; text desc " va trier les derniers nuls dans MySQL.

Je sais que c’est un ancien fil de discussion, mais j’ai une méthode un peu désordonnée qui est beaucoup plus rapide et personnalisable. En termes de rapidité, je dirais 10 s au lieu de 100 s (10: 1).

Ma méthode nécessite tout ce que vous voulez éviter de désordre :

  • Grouper par (et avoir)
  • groupe concat avec ORDER BY
  • 2 tables temporaires
  • utiliser des fichiers sur le disque!
  • en quelque sorte (php?) supprimant le fichier après

Mais quand vous parlez de MILLIONS (ou dans mon cas, des dizaines de millions), ça vaut le coup.

de toute façon ce n’est pas grand chose parce que les commentaires sont en portugais mais voici mon exemple:

MODIFIER : si je reçois des commentaires, j'expliquerai comment cela fonctionne:)

START TRANSACTION;

DROP temporary table if exists to_delete;

CREATE temporary table to_delete as (
    SELECT
        -- escolhe todos os IDs duplicados menos os que ficam na BD
        -- A ordem de escolha dos IDs é dada por "ORDER BY campo_ordenacao DESC" em que o primeiro é o que fica
        right(
            group_concat(id ORDER BY campos_ordenacao DESC SEPARATOR ','),
            length(group_concat(id ORDER BY campos_ordenacao DESC SEPARATOR ',')) 
                - locate(",",group_concat(id ORDER BY campos_ordenacao DESC SEPARATOR ','))
        ) as ids,

        count(*) as c

    -- Tabela a eliminar duplicados
    FROM teste_dup

    -- campos a usar para identificar  duplicados
    group by test_campo1, test_campo2, teste_campoN
    having count(*) > 1 -- é duplicado
);

-- aumenta o limite desta variável de sistema para o máx 
SET SESSION group_concat_max_len=4294967295;

-- envia os ids todos a eliminar para um ficheiro
select group_concat(ids SEPARATOR ',') from to_delete INTO OUTFILE 'sql.dat';

DROP temporary table if exists del3;
create temporary table del3 as (select CAST(1 as signed) as ix LIMIT 0);

-- insere os ids a eliminar numa tabela temporaria a partir do ficheiro
load data infile 'sql.dat' INTO TABLE del3
LINES TERMINATED BY ',';

alter table del3 add index(ix);

-- elimina os ids seleccionados
DELETE teste_dup -- tabela 
from teste_dup -- tabela

join del3 on id=ix;

COMMIT;

vous pouvez supprimer toutes les entrées en double en utilisant cette requête simple. qui sélectionnera tous les enregistrements en double et les supprimera.

 DELETE i1 
FROM TABLE i1
LEFT JOIN TABLE i2
  ON i1.id = i2.id
 AND i1.colo = i2.customer_invoice_id
 AND i1.id < i2.id
WHERE i2.customer_invoice_id IS NOT NULL
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top