post icon

Eliminar filas duplicadas con SQL de una tabla

Es típico encontrarnos con casos en donde alguien se olvido de colocar una clave única en algún lugar y luego tenemos registros con columnas duplicadas — no me refiero precisamente a claves primarias pero también podría aplicar el caso — y necesitamos eliminarlos pero dejar siempre uno de cada valor único, no es muy compleja la lógica a utilizar pero es una pregunta muy frecuente de como realizar esta tarea.

Para comprender bien la operativa a realizar vamos a hacer un ejemplo muy sencillo, creamos una tabla del abecedario donde no deben existir letras repetidas y cada letra tendrá su PK númerica.

CREATE TABLE abecedario(
	id in] NOT NULL Primary Key,
	letra char(1) NOT NULL);

Insertamos 10 filas de ejemplo

id          letra
----------- -----
1           A
2           B
3           C
4           D
5           A
6           E
7           D
8           F
9           F
10          Z

(10 filas afectadas)

Para conocer cuales son las letras que estan repetidas podemos ejecutar este query que selecciona las letras y las cuenta, agrupa las letras permitiendo que muestre una sola vez la letra repetida y el al lado muestre la cantidad de repeticiones

select letra, COUNT(*) from abecedario
group by letra
having COUNT(*) > 1;

Pero esto no basta, solo nos muestra los valores repetidos. Si quisieramos eliminar todos funcionario en un delete, pero el objetivo es eliminar todos los valores duplicados pero dejar uno. Para ello obligatoriamente necesitamos que exista una columna que tenga un identificador único (por ejemplo una PK u otra llave única).
Primero visualizamos cuales serán las filas borradas:

select a1.id ,a1.letra from abecedario a1
inner join abecedario a2
on a1.letra = a2.letra
and a1.id > a2.id;

Lo que hizo ese query es crear dos conjuntos de datos de la misma tabla, y unirlas en las columnas que deberian ser unicas y filtrarlas por que no son iguales al primer valor repetido. Para eliminar las filas ejecutariamos lo siguiente:

delete from abecedario
where id in (
	select a1.id from abecedario a1
	inner join abecedario a2
	on a1.letra = a2.letra
	and a1.id > a2.id
);

Quedando así la tabla:

id          letra
----------- -----
1           A
2           B
3           C
4           D
6           E
8           F
10          Z

(7 filas afectadas)

Comentarios desde Facebook:

  1. Maritus Mozilla Firefox Windows
    22 julio 2012 at 21:52 #

    GeekZero..,excelente..,muchas gracias capo..!!

  2. yna Mozilla Firefox Windows
    12 julio 2012 at 14:39 #

    Hola, muy aporte, me estoy rompiendo la cabeza con esto porque no tengo una clave unica en la tabla, y es una tabla de informix la cual no hallo como meterle un autoincremental, habra alguna manera de hacerlo sin la clave unica, solo pregunto… ya estoy un poco tapada

    • GeekZero Google Chrome Windows
      12 julio 2012 at 14:50 #

      Si no tienes una columna que identifique unicamente a cada registro, tienes alguna combinacion de columnas que la identifiquen unicamente?

      • ysa Mozilla Firefox Windows
        13 julio 2012 at 11:08 #

        Me acorde que puedo usar el ROWID que es unico. :mrgreen:

  3. fernandoSarasty ARGENTINA Google Chrome Windows
    3 octubre 2011 at 12:43 #

    Hola. La solución que planteas es perfecta, muy simple, práctica y performante. Funciona 100%. Intente hacer de todo sin tener éxito.

    Muchas gracias!

  4. Marcial BOLIVIA Mozilla Firefox Windows
    28 julio 2011 at 19:42 #

    Gracias, me sirvio mucho la clave esta en el id y la comparacion
    En mysql seria:
    delete a from abecedario AS a,
    (select a1.id AS id from abecedario a1
    inner join abecedario a2
    on a1.letra = a2.letra
    and a1.id > a2.id) AS t
    where id = t.id;

Responder