Encuentra registros duplicados en MySQL

650

Quiero extraer registros duplicados en una base de datos MySQL. Esto se puede hacer con:

SELECT address, count(id) as cnt FROM list
GROUP BY address HAVING cnt > 1

Lo que resulta en:

100 MAIN ST    2

Me gustaría extraerlo para que muestre cada fila que es un duplicado. Algo como:

JIM    JONES    100 MAIN ST
JOHN   SMITH    100 MAIN ST

¿Alguna idea sobre cómo se puede hacer esto? Estoy tratando de evitar hacer el primero y luego buscar los duplicados con una segunda consulta en el código.

Chris Bartow
fuente

Respuestas:

684

La clave es reescribir esta consulta para que pueda usarse como una subconsulta.

SELECT firstname, 
   lastname, 
   list.address 
FROM list
   INNER JOIN (SELECT address
               FROM   list
               GROUP  BY address
               HAVING COUNT(id) > 1) dup
           ON list.address = dup.address;
Powerlord
fuente
69
Tenga cuidado con las subconsultas. Las subconsultas son / pueden ser ridículamente malas para los problemas de rendimiento. Si esto tiene que suceder con frecuencia y / o con muchos registros duplicados, consideraría mover el procesamiento fuera de la base de datos a un conjunto de datos.
bdwakefield
11
Es una subconsulta no correlacionada, por lo que no debería ser tan malo suponiendo que cualquiera de las consultas por sí sola no esté mal diseñada.
Mayıu
Encantador. Supongo que esta es la sintaxis en torno a "ERROR 1248 (42000): cada tabla derivada debe tener su propio alias"
doublejosh
3
Esta es la idea correcta, pero de nuevo, como se muestra a continuación, esto solo funciona si se garantiza que las direcciones estén estandarizadas ...
Matt
30
+1 con esta consulta puede encontrar duplicados pero también triplicados, cuadruplicados ... y así sucesivamente
albanx
352
SELECT date FROM logs group by date having count(*) >= 2
trt
fuente
55
Esta fue la consulta de trabajo más fácil de usar con Laravel. Solo tenía que agregar ->having(DB::raw('count(*)'), '>', 2)a la consulta. ¡Muchas gracias!
Kovah
1
Funciona bien con la tabla de 10 millones de filas. Esta debería ser la mejor respuesta
Terry Lin
13
Ten cuidado con esta respuesta. Solo devuelve uno de los duplicados. Si tiene más de 2 copias del mismo registro, no las verá todas, y después de eliminar el registro devuelto, todavía tendrá duplicados en su tabla.
Mikiko Jane
77
¿Por qué >=2? Solo useHAVING COUNT(*) > 1
BadHorsie
2
@TerryLin Teniendo en cuenta que esto en realidad no resuelve el problema declarado originalmente (que era cómo devolver todos los duplicados), no estoy de acuerdo.
Michael
198

¿Por qué no solo INNER SE UNE a la mesa consigo mismo?

SELECT a.firstname, a.lastname, a.address
FROM list a
INNER JOIN list b ON a.address = b.address
WHERE a.id <> b.id

Se necesita una DISTINCT si la dirección puede existir más de dos veces.

Rudolfson
fuente
20
También probé esto, y fue casi 6 veces más lento en comparación con la solución aceptada en mi situación (último MySQL, tabla de 120,000 filas). Esto podría deberse a que requiere una tabla temporal, ejecute una EXPLICACIÓN en ambos para ver las diferencias.
44
Cambié la última parte de la consulta para WHERE a.id > b.idfiltrar solo los duplicados más nuevos, de esa manera puedo hacer un DELETEdirectamente en el resultado. Cambie la comparación para enumerar los duplicados más antiguos.
Stoffe
1
Esto tardó 50 segundos en ejecutarse, la respuesta de @ doublejosh tardó 0,13 segundos.
antonagestam
Debo agregar que esta respuesta da respuestas duplicadas a pesar de DONDE, ya que en caso de que una dirección se triplique, las filas de salida se duplican. Si es cuádruple, creo que la respuesta se triplicará.
Wli
Probé esto en leetcode " leetcode.com/problems/duplicate-emails ". Fue más rápido en comparación con la subconsulta.
billow
56

Intenté la mejor respuesta elegida para esta pregunta, pero me confundió un poco. En realidad lo necesitaba solo en un solo campo de mi mesa. El siguiente ejemplo de este enlace funcionó muy bien para mí:

SELECT COUNT(*) c,title FROM `data` GROUP BY title HAVING c > 1;
Arman Malik
fuente
¡Funciona de maravilla!
Vinícius
47
select `cityname` from `codcities` group by `cityname` having count(*)>=2

Esta es la consulta similar que ha solicitado y es 200% funcional y también fácil. ¡¡¡Disfrutar!!!

Pratswinz
fuente
37

¿No es esto más fácil?

SELECT *
FROM tc_tariff_groups
GROUP BY group_id
HAVING COUNT(group_id) >1

?

Tudor
fuente
1
funcionó para mí donde solo tenía que procesar ~ 10 000 filas duplicadas para hacerlas únicas, mucho más rápido que cargar todas las 600 000 filas.
adrianTNT
1
mucho más fácil
Shwet
35

Encuentre usuarios duplicados por dirección de correo electrónico con esta consulta ...

SELECT users.name, users.uid, users.mail, from_unixtime(created)
FROM users
INNER JOIN (
  SELECT mail
  FROM users
  GROUP BY mail
  HAVING count(mail) > 1
) dupes ON users.mail = dupes.mail
ORDER BY users.mail;
doublejosh
fuente
2
Para encontrar el duplicado real solo necesita la consulta interna. Esto es mucho más rápido que las otras respuestas.
antonagestam
20

podemos encontrar que los duplicados dependen de más de un campo también. Para esos casos, puede usar el siguiente formato.

SELECT COUNT(*), column1, column2 
FROM tablename
GROUP BY column1, column2
HAVING COUNT(*)>1;
KESAVAN PURUSOTHAMAN
fuente
16

Encontrar direcciones duplicadas es mucho más complejo de lo que parece, especialmente si necesita precisión. Una consulta MySQL no es suficiente en este caso ...

Trabajo en SmartyStreets , donde abordamos la validación y la desduplicación y otras cosas, y he visto muchos desafíos diversos con problemas similares.

Hay varios servicios de terceros que marcarán duplicados en una lista para usted. Hacer esto únicamente con una subconsulta MySQL no tendrá en cuenta las diferencias en los formatos y estándares de dirección. El USPS (para la dirección de EE. UU.) Tiene ciertas pautas para establecer estos estándares, pero solo un puñado de proveedores están certificados para realizar tales operaciones.

Por lo tanto, recomendaría la mejor respuesta para usted es exportar la tabla a un archivo CSV, por ejemplo, y enviarla a un procesador de listas capaz. Una de ellas es SmartyStreets Bulk Address Validation Tool, que lo hará por usted en unos segundos a unos minutos automáticamente. Marcará filas duplicadas con un nuevo campo llamado "Duplicar" y un valor Yen él.

Mate
fuente
66
+1 para ver la dificultad involucrada en la coincidencia de cadenas de direcciones, aunque es posible que desee especificar que la pregunta de "registros duplicados" del OP no es compleja en sí misma, sino que es cuando se comparan direcciones
historia
13

Otra solución sería usar alias de tabla, así:

SELECT p1.id, p2.id, p1.address
FROM list AS p1, list AS p2
WHERE p1.address = p2.address
AND p1.id != p2.id

Todo lo que realmente está haciendo en este caso es tomar el original lista de la tabla, la creación de dos p mesas retend - p 1 y p 2 - Fuera de eso, y luego realizar una combinación en la columna de la dirección (línea 3). La cuarta línea se asegura de que el mismo registro no aparezca varias veces en su conjunto de resultados ("duplicados duplicados").

jerdiggity
fuente
1
Funciona bien Si WHERE está comprobando con LIKE, también se encuentran apóstrofes. Hace la consulta más lenta, pero en mi caso es de una sola vez.
gossi
10

No va a ser muy eficiente, pero debería funcionar:

SELECT *
FROM list AS outer
WHERE (SELECT COUNT(*)
        FROM list AS inner
        WHERE inner.address = outer.address) > 1;
Abedul Chad
fuente
10

Esto seleccionará duplicados en una pasada de tabla, sin subconsultas.

SELECT  *
FROM    (
        SELECT  ao.*, (@r := @r + 1) AS rn
        FROM    (
                SELECT  @_address := 'N'
                ) vars,
                (
                SELECT  *
                FROM
                        list a
                ORDER BY
                        address, id
                ) ao
        WHERE   CASE WHEN @_address <> address THEN @r := 0 ELSE 0 END IS NOT NULL
                AND (@_address := address ) IS NOT NULL
        ) aoo
WHERE   rn > 1

Esta consulta emula realmente ROW_NUMBER()presente en OracleySQL Server

Vea el artículo en mi blog para más detalles:

Quassnoi
fuente
20
No FROM (SELECT ...) aooes una trampa, pero es una subconsulta :-P
Rocket Hazmat
8

Esto también le mostrará cuántos duplicados tiene y ordenará los resultados sin combinaciones.

SELECT  `Language` , id, COUNT( id ) AS how_many
FROM  `languages` 
GROUP BY  `Language` 
HAVING how_many >=2
ORDER BY how_many DESC
Martin Tonev
fuente
perfecto porque todavía dice cuántas entradas están duplicadas
denis
4
 SELECT firstname, lastname, address FROM list
 WHERE 
 Address in 
 (SELECT address FROM list
 GROUP BY address
 HAVING count(*) > 1)
Ryan Roper
fuente
Intenté este también, pero parece que se cuelga. Cree que el retorno de la consulta interna no satisface el formato del parámetro IN.
doublejosh
¿Qué quiere decir que no satisface el formato de parámetro? Todo lo que necesita IN es que su subconsulta tiene que devolver una sola columna. Es realmente bastante simple. Es más probable que su subconsulta se genere en una columna que no está indexada, por lo que se tarda una cantidad excesiva de tiempo en ejecutarse. Sugeriría si lleva mucho tiempo dividirlo en dos consultas. Tome la subconsulta, ejecútela primero en una tabla temporal, cree un índice en ella y luego ejecute la consulta completa haciendo la subconsulta donde está su campo duplicado en la tabla temporal.
Ryan Roper el
Me preocupaba que IN requiriera una lista separada por comas en lugar de una columna, lo cual era simplemente incorrecto. Aquí está la consulta que funcionó para mí:SELECT users.name, users.uid, users.mail, from_unixtime(created) FROM users INNER JOIN ( SELECT mail FROM users GROUP BY mail HAVING count(mail) > 1 ) dup ON users.mail = dup.mail ORDER BY users.mail, users.created;
doublejosh
4
select * from table_name t1 inner join (select distinct <attribute list> from table_name as temp)t2 where t1.attribute_name = t2.attribute_name

Para tu mesa sería algo así como

select * from list l1 inner join (select distinct address from list as list2)l2 where l1.address=l2.address

Esta consulta le dará todas las entradas de dirección distintas en su tabla de lista ... No estoy seguro de cómo funcionará si tiene valores de clave principal para el nombre, etc.

Neha Patil
fuente
4

Procedimiento de consultas de eliminación de duplicados más rápido:

/* create temp table with one primary column id */
INSERT INTO temp(id) SELECT MIN(id) FROM list GROUP BY (isbn) HAVING COUNT(*)>1;
DELETE FROM list WHERE id IN (SELECT id FROM temp);
DELETE FROM temp;
venkatesh
fuente
2
Obviamente, esto elimina solo el primer registro de cada grupo de duplicados.
Palec
4

Personalmente, esta consulta ha resuelto mi problema:

SELECT `SUB_ID`, COUNT(SRV_KW_ID) as subscriptions FROM `SUB_SUBSCR` group by SUB_ID, SRV_KW_ID HAVING subscriptions > 1;

Lo que hace este script es mostrar todos los ID de suscriptor que existen más de una vez en la tabla y la cantidad de duplicados encontrados.

Estas son las columnas de la tabla:

| SUB_SUBSCR_ID | int(11)     | NO   | PRI | NULL    | auto_increment |
| MSI_ALIAS     | varchar(64) | YES  | UNI | NULL    |                |
| SUB_ID        | int(11)     | NO   | MUL | NULL    |                |    
| SRV_KW_ID     | int(11)     | NO   | MUL | NULL    |                |

¡Espero que sea útil para ti!

Ionut Petre
fuente
3
SELECT t.*,(select count(*) from city as tt where tt.name=t.name) as count FROM `city` as t where (select count(*) from city as tt where tt.name=t.name) > 1 order by count desc

Reemplace la ciudad con su mesa. Reemplace el nombre con el nombre de su campo

Lalit Patel
fuente
2
    SELECT *
    FROM (SELECT  address, COUNT(id) AS cnt
    FROM list
    GROUP BY address
    HAVING ( COUNT(id) > 1 ))
DJ.
fuente
0
    Find duplicate Records:

    Suppose we have table : Student 
    student_id int
    student_name varchar
    Records:
    +------------+---------------------+
    | student_id | student_name        |
    +------------+---------------------+
    |        101 | usman               |
    |        101 | usman               |
    |        101 | usman               |
    |        102 | usmanyaqoob         |
    |        103 | muhammadusmanyaqoob |
    |        103 | muhammadusmanyaqoob |
    +------------+---------------------+

    Now we want to see duplicate records
    Use this query:


   select student_name,student_id ,count(*) c from student group by student_id,student_name having c>1;

+--------------------+------------+---+
| student_name        | student_id | c |
+---------------------+------------+---+
| usman               |        101 | 3 |
| muhammadusmanyaqoob |        103 | 2 |
+---------------------+------------+---+
Usman Yaqoob
fuente
0

Para ver rápidamente las filas duplicadas, puede ejecutar una única consulta simple

Aquí estoy consultando la tabla y enumerando todas las filas duplicadas con el mismo user_id, market_place y sku:

select user_id, market_place,sku, count(id)as totals from sku_analytics group by user_id, market_place,sku having count(id)>1;

Para eliminar la fila duplicada, debe decidir qué fila desea eliminar. Por ejemplo, el que tiene un ID más bajo (generalmente más antiguo) o tal vez alguna otra información de fecha. En mi caso, solo quiero eliminar la identificación más baja, ya que la identificación más nueva es la información más reciente.

Primero verifique si se eliminarán los registros correctos. Aquí estoy seleccionando el registro entre los duplicados que se eliminarán (por identificación única).

select a.user_id, a.market_place,a.sku from sku_analytics a inner join sku_analytics b where a.id< b.id and a.user_id= b.user_id and a.market_place= b.market_place and a.sku = b.sku;

Luego ejecuto la consulta de eliminación para eliminar los engaños:

delete a from sku_analytics a inner join sku_analytics b where a.id< b.id and a.user_id= b.user_id and a.market_place= b.market_place and a.sku = b.sku;

Copia de seguridad, doble verificación, verificar, verificar la copia de seguridad y luego ejecutarla.

Ganesh Krishnan
fuente
-1

select address from list where address = any (select address from (select address, count(id) cnt from list group by address having cnt > 1 ) as t1) order by address

la subconsulta interna devuelve filas con una dirección duplicada, luego la subconsulta externa devuelve la columna de dirección para la dirección con duplicados. la subconsulta externa debe devolver solo una columna porque se usó como operando para el operador '= any'

aad
fuente
-1

Respuesta de Powerlord es la mejor y recomendaría un cambio más: use LIMIT para asegurarse de que db no se sobrecargue:

SELECT firstname, lastname, list.address FROM list
INNER JOIN (SELECT address FROM list
GROUP BY address HAVING count(id) > 1) dup ON list.address = dup.address
LIMIT 10

Es una buena costumbre usar LIMIT si no hay DONDE y al hacer uniones. Comience con un valor pequeño, verifique qué tan pesada es la consulta y luego aumente el límite.

Michał Maluga
fuente
¿Cómo está contribuyendo esto a algo?
Kennet Celeste