Como borrar registros duplicados de SQL (the ultimate solution)

El dia de hoy en mi trabajo se me presento un problema con registros duplicados de una tabla de SQL server, asi que me di a la tarea que aqueja a todo DBA alguna vez en su vida. Como borrar registros duplicados de una tabla?  (yo no soy DBA, por cierto, pero aqui hay que hacerla de todo).

En principio de cuentas, una registro duplicado nos habla de un mal diseño, o de base de datos o de procedimientos. En este caso el problema es un error de procedimiento, pero por ahora no hablaremos de eso.

Mi caso esta asi: Tengo una tabla con ID numerico, y un numero x de campos. De esos campos, los registros duplicados son iguales a excepcion de 1 campo (aparte del ID, por supuesto). La tabla tiene algo mas de 4 millones de registros, y habia unos 400,000 duplicados (algunos mas de 6 veces).

Googleando un poco encontre varias soluciones que van desde hacer un Select distinct a una tabla temporal, vaciar la tabla original y luego copiar lo de la temporal a la original (lo cual resulta muy lento), hasta un script que hacia uso de cursores, y que nunca pude hechar a andar.

Todo fue hasta que por ahi me encontre con una solucion simple, rapida y efectiva, que solo requiere que tu tabla contenga un ID numerico. Si no lo tiene, pues lo puedes agregar facilmente, y si despues no lo requieres, puedes borrarlo.

De lo que se trata es de seleccionar la ID mas baja (o mas alta) de cada conjunto de duplicados (tenemos que definir que es un duplicado), y borrar las demas. Simple, no?

Para ejemplificarlo vamos a suponer que tenemos nuestra tabla “Tabla1″.  Esta tabla tiene los campos “ID”, “Campo1″, Campo2″ y “Campo3″. Lo primero que tenemos que hacer es identificar que campos tienen que ser iguales para que un registro sea repetido. Puede ser que sean todos los campos o solo un conjunto de estos. En este ejemplo vamos a decir que Campo2 y 3 son necesarios. Una vez identificados lo que tenemos que hacer es ejecutar la siguiente consulta.

 delete from Tabla1 where Id >
(
Select min(Id) from Tabla1 Tbl1 where Tabla1.Campo2 = Tbl1.Campo2 and Tabla1.Campo3 = Tbl1.Campo3
)

Es todo! Simple, no? Funciona igual si cambias el signo por “<” y  usas “max” en lugar de “min”. En este caso en lugar de conservar el Id mas baja, conservas la mas alta.

Simple y rapido ademas. Mi consulta demoro poco mas de un minuto en buscar y borrar los duplicados en mi tabla de 4 millones. Espero que esta solucion los salve de mas de una. ∞

42 Responses to “Como borrar registros duplicados de SQL (the ultimate solution)”

  1. Carlos Paredes Says:

    Excelente solución, mil gracias!!!

  2. SAMUEL Says:

    excelentisimo, me ayudo bastante

    muchas gracias

    y felicidades por tu website.

  3. ayudam Says:

    a mi me sale error no se porque ,,, porfa ayudam..por ejemplo:
    select min(id) from ventas Tv
    where
    ventas.cliente = Tv.cliente(hay mas columnas pero el error es igual)
    no me hace caso y sale error:

    Server: Msg 107, Level 16, State 3, Line 1
    The column prefix ‘ventas’ does not match with a table name or alias name used in the query.

    no compara ni filtra los dup
    gracias

  4. Delo Says:

    Esta solución está bien para un caso normal (sin tablas mal diseñadas). La verdad es que es útil y sencillo.
    Lo malo es si el campo clave no está definido como tal, y está duplicado también; en ese caso no serviría… y esto pasa a menudo, por desgracia.
    Entonces si que tendrás que pasar lo bueno a una tabla temporal, etc.

  5. admin Says:

    No, de hecho, sin tablas mal diseñadas, de hecho, no habria necesidad de esto, pues una tabla bien diseñada segun las 3 formas normales, no deberia de tener registros duplicados.

    Si hay un campo llave mal definido, pues de hecho es como si no existiera uno. Y la tecnica dice que hay que agregar una nueva columna numerica que nos servira como llave temporal para aplicar esta tecnica

  6. aballesteg Says:

    Excelente solución!!!

  7. aballesteg Says:

    Excelente solución!!!! Me gustaría saber cuales fueron tus fuentes de consulta para llegar a esto tan simple.

  8. admin Says:

    El simple y confiable Google!

  9. Potosino Says:

    Esta solucion funciona, pero!!!!
    Si hablaramos de una tabla que tiene un campo clave que se repite y la diferencia de cada registro es una fecha, y solo quisiera dejar los registros sinrepetir con fecha mas alta?

    Ya no es tan sencillo verdad?
    :(

    Si alguien sabe como hacer esto se los agradecere eternamente.

    Saludos desde SLP México

  10. admin Says:

    No, de hecho es exactamente lo mismo. Lo unico que tienes que hacer es agregar otro campo a la tabla, autonumerico para asegurarte que sea unico, y basar en el el borrado de duplicados.

  11. Metalkore Says:

    Exelente solucion.
    Aunque no la utilice para eliminar si no para hacer una consulta
    igual se agradece, me sirvio mucho

  12. carlos flores Says:

    muchas gracias, realamente me ayudo bastante

  13. ISABEL Says:

    Perfecto!!! me has ayudado mucho, también había encontrado la solcuión de la tabla temporal, pero como que no, era demasiado complicado, ésta sin embargo es…. tan simple…

  14. santiago Says:

    se pueden apoyar de rowid como campo clave, este segun yo no se repite

  15. Alfonso Says:

    Hace mas de un año postease esto

    y no sabes como me acabas de salvar la vida

    se pura casualidad la tabla tenia un idconsecutivo

    y me quedo como anillo al dedo

    por que yo no hubiera podido modificar la tabla sin pedir permiso a media empresa

    Saludos

  16. Diego Says:

    Exelente solucion …. hay que tener en cuenta que para sqlserver se presenta el error que reportaron antes, simplemente hay que cambiar el nombre de la tabla por el alias que se asignó a la misma

  17. Rocio Says:

    Excelente!!!

    Me ha funcionado a la perfeccion.

  18. Jano Mendoza Says:

    muchas gracias por la ayuda, me sirvio bastante ya que el distinct no sirve cuando los registros tienen fechas

  19. Rocio Says:

    Y si tuviera que borrar los de mayor id pero guardar un campo y hacer el update de ese campo en la fila que nos quedamos, se podria??

    Gracias.

  20. ariel Says:

    brillante muy bueno unos pocos segundo y solucionado

  21. Marcelo Says:

    Muy buena solucion, pero eso nos sirve cuando existen 2 campos duplicados en un mismo registro, pero no para el caso cuando tenemos ID duplicadas, o dicho de otra manera, cuando hay 2 o mas clientes con el mismo ID, eso generalmente llega cuando se reciben bases de datos sin normalizar y hay que insertar o actualizar en una base de datos normalizada.

    Saludos

  22. ricardo Says:

    Muchisimas gracias… un gran aporte de tu parte

  23. JOSE Says:

    Tengo una pregunta, si la tabla que quieres limpiar de duplicacos tuviera un parametro mas, una fecha, es decir, que tuvieras que tener en cuenta la fecha para la eliminacion de las filas.

    Ejemplo:
    Eliminar todos los duplicados de X codigo de Barras, solo quedando la fila con la fecha mas actual.

  24. JOSE Says:

    SOLUCIONADO, jeje, solo cambie el ID por un campo fecha y Listo, quedo asi:

    delete from Tabla where FECHA

  25. Marcelo Says:

    Me sacaste de un tremendo apuro…Gracias.

  26. Mario Says:

    Para Potosino:

    Funciona igual si colocas omo min o max la fecha, yo tuve ese problema y con la ayuda de este excelente query resolví eliminar los registros repetidos donde mi común era un ID único, y el campo de referencia para la eliminación de los registros duplicados es la fecha mayor, y que se quedara el registro que tuviera la fecha menor. quedó asi:

    delete from tabla where fecha >
    (
    Select min(fecha) from tabla Tbl1
    where tabla.id = Tbl1.id
    )

    Excelente query el que compartes amigo, simplemente excelente.

  27. nama Says:

    funciona a la perfección :) yo le tuve que hacer unas adecuaciones pero el resultado fue optimo ;)

  28. Erick Says:

    esta mal esta query, me borro todos los registros que se repetian, no me dejo nada.

  29. jeffrey Says:

    Y en caso, no tenga un id porque no han normalizado la bd…como puedo recorrer cada celda pero sin usar cursores…ya que esto pone lenta la aplicacion

  30. Axel_Tech Says:

    Perfecto! Justo lo que buscaba! Gracias

  31. yue Says:

    de donde saca Tbl1???

  32. Alex Says:

    Disculpen la ignorancia, pero Tbl1 a que hace referencia???

  33. guillermo Says:

    Funciona bien!! pero la verdad que no entiendo porque.
    Se supone que el delete externo exterminará todo los de ID mayor… TODOS, sin importar si son repetidos o no, ya que la comparación se hace en la subconsulta, pero no…. funciona ok.
    Alguien me lo aclara?

  34. lucy Says:

    Hola…al igual q alex..no entiendo a que hace referencia tbl1? por favor me lo aclaras?? Gracias

  35. Cerrowulf Says:

    El Tbl1 se refiere a Tabla1, solo que Tbl1 funciona como un alias

  36. Karr Says:

    Muchísimas gracias por esta solución es fantástica.

  37. Carlos Says:

    la mejor solucion en todo google :D perfecto amigo.

  38. David Says:

    hola a todos
    este es mi query
    SELECT IdPersona,AlumPersona,AcudPersona
    FROM cl_persona
    WHERE IdPersona >
    ( SELECT min(IdPersona) FROM cl_persona bl1
    WHERE cl_persona.AlumPersona=bl1.AlumPersona
    and cl_persona.AcudPersona=bl1.AcudPersona );

    El query funciona con SELECT pero cuando lo hago con DELETE sale esto:
    Error Code : 1093
    You can’t specify target table ‘cl_persona’ for update in FROM clause
    (0 ms taken)

  39. Jhonatan Says:

    Estas seguro q funciona esta solucion??, me parece que no
    te dire porque:
    Si mi tabla tiene estos registros

    Id campo 1
    01 rojo
    01 rojo
    02 amarillo
    02 amarillo
    03 verde

    hasta ahora no se como sacando el “minimo” vas a eliminar registros repetidos.

  40. Elita Says:

    Hola a todos:

    A mi me facilito detectar los registros duplicados con la función HAVING. Allí les dejo un ejemplo

    SELECT c1,c2 FROM MiTabla
    GROUP BY c1,c2
    HAVING count(*) > 1

  41. Pablo Says:

    Muy buen script. La tabla en la cual requería eliminar los registros duplicados no tiene un campo identity así que filtré los registros duplicados, los almacené en una tabla temporal, apliqué tu script y luego generé una sentencia delete usando un select a la tabla temporal.
    Gracias

  42. Airguana Says:

    Exelente, funciona a la perfeccion, en mi caso cree un indice agrupado para ordenar los registros duplicados fisicamente, luego añadi un campo identity, ejecute el script y elimine el identity, y listo….

Leave a Reply