Domanda

Ho un database MySQL di grandi dimensioni (> Mil) incasinato da duplicati. Penso che potrebbe essere da 1/4 a 1/2 dell'intero db riempito con loro. Devo liberarmene rapidamente (intendo i tempi di esecuzione delle query). Ecco come appare:
id (indice) | testo1 | testo2 | text3
text1 & amp; la combinazione text2 dovrebbe essere unica, se ci sono duplicati, dovrebbe rimanere solo una combinazione con text3 NOT NULL. Esempio:

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

... diventa:

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)

I nuovi ID a freddo sono qualsiasi cosa, non dipendono dai vecchi ID di tabella.
Ho provato cose come:

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;

Oppure SELEZIONA DISTINCT e altre varianti.
Mentre lavorano su piccoli database, il tempo di esecuzione delle query sul mio è semplicemente enorme (in realtà non è mai arrivato alla fine; > 20 min)

C'è un modo più veloce per farlo? Aiutatemi a risolvere questo problema.

È stato utile?

Soluzione

Credo che lo farà, usando su chiave duplicata + 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;

Dovrebbe essere molto più veloce di qualsiasi cosa richieda raggruppamento o distinto o una sottoquery, o anche ordine per. Questo non richiede nemmeno un fileort, che sta per uccidere le prestazioni su una grande tabella temporanea. Richiederà comunque una scansione completa sulla tabella originale, ma non è possibile evitarlo.

Altri suggerimenti

Ho trovato questo semplice codice a 1 riga per fare esattamente ciò di cui avevo bisogno:

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

Tratto da: 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
)

Questo interroga tutti i record, i gruppi per i campi di distinzione e gli ordini per ID (significa che selezioniamo il primo record text3 non nullo). Quindi selezioniamo gli ID da quel risultato (questi sono buoni ID ... non verranno eliminati) ed eliminiamo tutti gli ID che NON SONO quelli.

Qualsiasi query come questa che interessa l'intera tabella sarà lenta. Devi solo eseguirlo e lasciarlo distribuire in modo da poterlo prevenire in futuro.

Dopo aver eseguito questa operazione " fix " Vorrei applicare UNICO INDICE (testo1, testo2) a quella tabella. Per prevenire la possibilità di duplicati in futuro.

Se vuoi passare a " crea una nuova tabella e sostituisci quella precedente " itinerario. È possibile utilizzare l'istruzione select molto interna per creare l'istruzione insert.

Specifiche di MySQL (presuppone che la nuova tabella sia denominata my_tbl2 e abbia esattamente la stessa struttura):

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

Vedi INSERTO MySQL ... SELEZIONA per ulteriori informazioni.

rimuove i duplicati senza rimuovere le chiavi esterne

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);

Se puoi creare una nuova tabella, fallo con una chiave univoca nei campi text1 + text2. Quindi inserire nella tabella ignorando gli errori (usando la sintassi INSERT IGNORE):

select * from my_tbl order by text3 desc
  • Penso che l'ordine per text3 desc metterà gli NULL per ultimi, ma ricontrollalo.

Gli indici su tutte quelle colonne potrebbero aiutare molto, ma crearli ora potrebbe essere piuttosto lento.

Per le tabelle di grandi dimensioni con pochi duplicati, potresti voler evitare di copiare l'intera tabella in un'altra posizione. Un modo è quello di creare una tabella temporanea contenente le righe che si desidera conservare (per ogni chiave con duplicati), quindi eliminare i duplicati dalla tabella originale.

Viene fornito un esempio qui .

Non ho molta esperienza con MySQL. Se ha funzioni analitiche, prova:

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 clausola facoltativa where rende il mezzo che dovrai eseguire più volte, una per ogni lettera, ecc. Creare un indice su text1?

Prima di eseguire questo, confermare che " text desc " ordinerà i valori nulli per ultimi in MySQL.

So che questo è un vecchio thread ma ho un metodo un po ' disordinato che è molto più veloce e personalizzabile, in termini di velocità direi 10sec anziché 100sec (10: 1).

Il mio metodo ha richiesto tutte quelle disordinate cose che stavi cercando di evitare:

  • Raggruppa per (e avendo)
  • gruppo concat con ORDER BY
  • 2 tabelle temporanee
  • usando i file su disco!
  • in qualche modo (php?) eliminando il file dopo

Ma quando parli di MILIONI (o nel mio caso Decine di milioni) ne vale la pena.

comunque non è molto perché i commenti sono in portoghese ma ecco il mio esempio:

MODIFICA : se ricevo commenti spiegherò ulteriormente come funziona :)

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;

puoi rimuovere tutte le voci duplicate usando questa semplice query. che selezionerà tutti i record duplicati e li rimuoverà.

 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top