Pregunta

Tengo una gran base de datos MySQL (> Mil filas) desordenada por duplicados. Creo que podría ser de 1/4 a 1/2 de toda la base de datos llena de ellos. Necesito deshacerme de ellos rápidamente (me refiero al tiempo de ejecución de la consulta). Así es como se ve:
id (índice) | texto1 | text2 | text3
texto1 & amp; La combinación text2 debe ser única, si hay duplicados, solo debe quedar una combinación con text3 NOT NULL. Ejemplo:

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

... se convierte en:

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)

Los nuevos identificadores pueden ser cualquier cosa, no dependen de los identificadores de tabla antiguos.
He intentado cosas como:

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;

O SELECCIONE DISTINCT y otras variaciones.
Mientras trabajan en bases de datos pequeñas, el tiempo de ejecución de consultas en el mío es enorme (nunca llegó al final, en realidad; > 20 min)

¿Hay alguna forma más rápida de hacer eso? Por favor, ayúdame a resolver este problema.

¿Fue útil?

Solución

Creo que esto lo hará, usando una clave duplicada + 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;

Debería ser mucho más rápido que cualquier cosa que requiera agrupar o distinguir o una subconsulta, o incluso ordenar por. Esto ni siquiera requiere una clasificación de archivos, lo que va a matar el rendimiento en una tabla temporal grande. Todavía requerirá un escaneo completo sobre la tabla original, pero no se puede evitar eso.

Otros consejos

Encontré este simple código de 1 línea para hacer exactamente lo que necesitaba:

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

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

Esto consulta todos los registros, grupos por campos de distinción y pedidos por ID (significa que elegimos el primer registro de texto no nulo3). Luego seleccionamos los ID de ese resultado (estos son buenos ID ... no se eliminarán) y eliminamos todos los ID que NO SON esos.

Cualquier consulta como esta que afecte a toda la tabla será lenta. Solo necesita ejecutarlo y dejar que se despliegue para que pueda evitarlo en el futuro.

Después de haber hecho esto " corregir " Aplicaría ÍNDICE ÚNICO (text1, text2) a esa tabla. Para evitar la posibilidad de duplicados en el futuro.

Si desea ir a " crear una nueva tabla y reemplazar la anterior " ruta. Puede usar la declaración select muy interna para crear su declaración de inserción.

MySQL específico (asume que la nueva tabla se llama my_tbl2 y tiene exactamente la misma estructura):

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

Consulte MySQL INSERT ... SELECT para más información.

eliminar duplicados sin eliminar claves foráneas

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 puede crear una nueva tabla, hágalo con una clave única en los campos text1 + text2. Luego inserte en la tabla ignorando errores (usando la sintaxis INSERT IGNORE):

select * from my_tbl order by text3 desc
  • Creo que el orden por text3 desc pondrá los NULL al final, pero verifíquelo dos veces.

Los índices en todas esas columnas podrían ayudar mucho, pero crearlos ahora podría ser bastante lento.

Para tablas grandes con pocos duplicados, puede evitar copiar toda la tabla a otro lugar. Una forma es crear una tabla temporal que contenga las filas que desea mantener (para cada clave con duplicados) y luego eliminar duplicados de la tabla original.

Se da un ejemplo aquí .

No tengo mucha experiencia con MySQL. Si tiene funciones analíticas, intente:

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 cláusula where opcional hace que tengas que ejecutarla varias veces, una para cada letra, etc. ¿Crear un índice en text1?

Antes de ejecutar esto, confirme que " text desc " clasificará nulos en MySQL.

Sé que este es un hilo antiguo, pero tengo un método algo desordenado que es mucho más rápido y personalizable, en términos de velocidad, diría que 10 segundos en lugar de 100 segundos (10: 1).

Mi método requiere todas esas cosas desordenadas que intentabas evitar:

  • Agrupar por (y Tener)
  • grupo concat con ORDER BY
  • 2 tablas temporales
  • usando archivos en el disco!
  • de alguna manera (php?) eliminando el archivo después

Pero cuando estás hablando de MILLONES (o en mi caso, Decenas de Millones) vale la pena.

de todos modos no es mucho porque los comentarios están en portugués pero aquí está mi muestra:

EDITAR : si recibo comentarios, explicaré más a fondo cómo funciona :)

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;

puede eliminar todas las entradas duplicadas mediante esta simple consulta. eso seleccionará todos los registros duplicados y los eliminará.

 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top