Encontre registros duplicados no MySQL


650

Quero extrair registros duplicados em um banco de dados MySQL. Isso pode ser feito com:

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

O que resulta em:

100 MAIN ST    2

Gostaria de puxá-lo para que ele mostre cada linha que é uma duplicata. Algo como:

JIM    JONES    100 MAIN ST
JOHN   SMITH    100 MAIN ST

Alguma idéia de como isso pode ser feito? Estou tentando evitar fazer o primeiro e depois procurar as duplicatas com uma segunda consulta no código.

Respostas:


684

A chave é reescrever esta consulta para que possa ser usada como uma 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;

69
Cuidado com as subconsultas. As subconsultas são / podem ser ridiculamente ruins por questões de desempenho. Se isso acontecer com frequência e / ou com muitos registros duplicados, eu consideraria mover o processamento para fora do banco de dados e para um conjunto de dados.
Bdwakefield 12/05/09

11
É uma subconsulta não correlacionada, portanto, não deve ser muito ruim, supondo que uma consulta sozinha não seja mal projetada.
11/05/09

Encantador. Acho que este é o sytax em torno de "ERROR 1248 (42000): Cada derivado tabela deve ter o seu próprio apelido"
doublejosh

3
Esta é a idéia certa, mas, novamente, como abaixo, isso só funciona se os endereços são garantidos para ser padronizado ...
Matt

30
+1 com esta consulta pode encontrar duplicatas, mas também triplicatas, quadruplicatas ..... e assim por diante
albanx

352
SELECT date FROM logs group by date having count(*) >= 2

5
Essa foi a consulta de trabalho mais fácil de usar com o Laravel. Só tinha que adicionar ->having(DB::raw('count(*)'), '>', 2)à consulta. Muito Obrigado!
Kovah

1
Funciona bem com 10 milhões de linhas de tabela. Esta deve ser a melhor resposta
Terry Lin

13
Tenha cuidado com esta resposta. Retorna apenas uma das duplicatas. Se você tiver mais de 2 cópias do mesmo registro, não as verá todas e, após excluir o registro retornado, ainda haverá duplicatas na sua tabela.
Mikiko Jane

7
Por que >=2? Apenas use #HAVING COUNT(*) > 1
BadHorsie 16/16/16

2
@TerryLin Considerando que isso realmente não resolve o problema originalmente declarado (que era como retornar todas as duplicatas), eu discordo.
Michael

198

Por que não apenas INNER JOIN a tabela consigo?

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

Um DISTINCT é necessário se o endereço existir mais de duas vezes.


20
Eu também testei isso, e foi quase 6 vezes mais lento em comparação com a solução aceita na minha situação (mais recente MySQL, tabela de 120.000 linhas). Isso pode ser devido ao fato de exigir uma tabela temporária, execute um EXPLAIN em ambos para ver as diferenças.

4
Alterei a última parte da consulta para WHERE a.id > b.idfiltrar apenas as duplicatas mais recentes, para que eu possa fazer uma pesquisa DELETEdiretamente no resultado. Alterne a comparação para listar as duplicatas mais antigas.
Stoffe

1
Demorou 50 segundos para ser executado, a resposta de @ doublejosh levou 0,13 segundos.
Antonagestam

Devo acrescentar que essa resposta fornece respostas duplicadas, apesar do WHERE, pois no caso de um endereço ser triplicado, as linhas de saída são duplicadas. Se for quádruplo, acredito que a resposta será triplicada.
Wli

Eu testei isso no leetcode " leetcode.com/problems/duplicate-emails ". Foi mais rápido em comparação com a subconsulta.
billow

56

Tentei a melhor resposta escolhida para esta pergunta, mas isso me confundiu um pouco. Na verdade, eu precisava disso apenas em um único campo da minha mesa. O exemplo a seguir deste link funcionou muito bem para mim:

SELECT COUNT(*) c,title FROM `data` GROUP BY title HAVING c > 1;

Funciona como um encanto!
Vinícius

47
select `cityname` from `codcities` group by `cityname` having count(*)>=2

Essa é a consulta semelhante que você solicitou e é 200% fácil e de trabalho também. Aproveitar!!!


37

Isso não é mais fácil:

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

?


1
trabalhou para mim onde eu tinha que processar ~ 10.000 linhas duplicadas para torná-las únicas, muito mais rápido do que carregar todas as 600.000 linhas.
precisa saber é o seguinte

1
muito mais fácil
Shwet

35

Encontre usuários duplicados por endereço de email com 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;

2
Para encontrar a duplicata real, você só precisa da consulta interna. Isso é muito mais rápido que as outras respostas.
Antonagestam

20

podemos encontrar as duplicatas depende de mais de um campo também. Para esses casos, você pode usar o formato abaixo.

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

16

Encontrar endereços duplicados é muito mais complexo do que parece, especialmente se você precisar de precisão. Uma consulta MySQL não é suficiente neste caso ...

Trabalho na SmartyStreets , onde lidamos com validação, desduplicação e outras coisas, e já vi muitos desafios diversos com problemas semelhantes.

Existem vários serviços de terceiros que sinalizam duplicatas em uma lista para você. Fazer isso somente com uma subconsulta do MySQL não levará em consideração diferenças nos formatos e padrões de endereço. O USPS (para endereço nos EUA) possui certas diretrizes para tornar esses padrões, mas apenas alguns fornecedores estão certificados para executar essas operações.

Portanto, eu recomendaria a melhor resposta para você exportar a tabela para um arquivo CSV, por exemplo, e enviá-la para um processador de lista capaz. Uma delas é a Ferramenta de Validação de Endereço em Massa do SmartyStreets, que fará isso em alguns segundos ou em alguns minutos automaticamente. Ele sinalizará linhas duplicadas com um novo campo chamado "Duplicar" e um valor Ynele.


6
+1 por ver a dificuldade envolvida na correspondência de cadeias de endereços, embora você queira especificar que a pergunta "registros duplicados" do OP não é complexa por si só, mas é ao comparar endereços
reportagem

13

Outra solução seria usar aliases de tabela, assim:

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

Tudo o que você está fazendo realmente, neste caso, está tomando o original lista mesa, criando dois p tabelas retend - p 1 e p 2 - fora disso, e em seguida, executar uma junção na coluna de endereço (linha 3). A quarta linha garante que o mesmo registro não seja exibido várias vezes no seu conjunto de resultados ("duplicatas duplicadas").


1
Works nice. Se o WHERE estiver verificando com LIKE, os apóstrofos também serão encontrados. Torna a consulta mais lenta, mas no meu caso, é um temporizador.
Gossi

10

Não será muito eficiente, mas deve funcionar:

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

10

Isso selecionará duplicatas em uma passagem de tabela, sem 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

Essa consulta emula ROW_NUMBER()atualmente presentes em OracleeSQL Server

Veja o artigo no meu blog para obter detalhes:


20
Não para nitpick, mas FROM (SELECT ...) aooé uma subconsulta :-P
Rocket Hazmat

8

Isso também mostrará quantas duplicatas existem e ordenará os resultados sem junções

SELECT  `Language` , id, COUNT( id ) AS how_many
FROM  `languages` 
GROUP BY  `Language` 
HAVING how_many >=2
ORDER BY how_many DESC

perfeito, porque ele ainda diz quantas entradas são duplicados
denis

4
 SELECT firstname, lastname, address FROM list
 WHERE 
 Address in 
 (SELECT address FROM list
 GROUP BY address
 HAVING count(*) > 1)

Tentei este também, mas parece apenas travar. Acredite que o retorno da consulta interna não satisfaz o formato do parâmetro IN.
DoubleJosh

O que você quer dizer com não satisfaz o formato do parâmetro? Tudo o que IN precisa é que sua subconsulta precise retornar uma única coluna. É realmente muito simples. É mais provável que sua subconsulta esteja sendo gerada em uma coluna que não está indexada, portanto está demorando muito tempo para ser executado. Sugiro que demore muito tempo para dividi-lo em duas consultas. Pegue a subconsulta, execute-a primeiro em uma tabela temporária, crie um índice e execute a consulta completa fazendo a subconsulta onde seu campo duplicado está na tabela temporária.
Ryan Roper

Eu estava preocupado em exigir uma lista separada por vírgula em vez de uma coluna, o que estava errado. Aqui está a consulta que funcionou para mim: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 sua mesa, seria algo como

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

Esta consulta fornecerá todas as entradas de endereço distintas na sua tabela de lista ... Não sei como isso funcionará se você tiver algum valor de chave primária para nome, etc.


4

Procedimento de consultas de remoção de duplicatas mais 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;

2
Obviamente, isso exclui apenas o primeiro registro de cada grupo de duplicatas.
Palec 15/02

4

Pessoalmente, esta consulta resolveu meu problema:

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

O que esse script faz é mostrar todos os IDs de assinantes que existem mais de uma vez na tabela e o número de duplicatas encontradas.

Estas são as colunas da tabela:

| 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 seja útil para você também!


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

Substitua cidade pela sua mesa. Substitua nome pelo nome do seu campo


2
    SELECT *
    FROM (SELECT  address, COUNT(id) AS cnt
    FROM list
    GROUP BY address
    HAVING ( COUNT(id) > 1 ))

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 |
+---------------------+------------+---+

0

Para ver rapidamente as linhas duplicadas, você pode executar uma única consulta simples

Aqui estou consultando a tabela e listando todas as linhas duplicadas com o mesmo user_id, market_place e 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 excluir a linha duplicada, você deve decidir qual linha deseja excluir. Por exemplo, aquele com ID inferior (geralmente mais antigo) ou talvez alguma outra informação de data. No meu caso, só quero excluir o ID inferior, pois o ID mais recente é a informação mais recente.

Primeiro verifique se os registros corretos serão excluídos. Aqui, estou selecionando o registro entre duplicatas que serão excluídas (por ID único).

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;

Em seguida, executo a consulta de exclusão para excluir os dupes:

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;

Backup, verifique, verifique, verifique o backup e execute.


-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

a subconsulta interna retorna linhas com endereço duplicado e a subconsulta externa retorna a coluna de endereço para o endereço com duplicatas. a subconsulta externa deve retornar apenas uma coluna porque foi usada como operando para o operador '= any'


-1

A resposta do Powerlord é realmente a melhor e eu recomendaria mais uma alteração: use LIMIT para garantir que o banco de dados não fique sobrecarregado:

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

É um bom hábito usar LIMIT se não houver ONDE e ao fazer junções. Comece com um valor pequeno, verifique o peso da consulta e aumente o limite.


como isso está contribuindo com alguma coisa?
Kennet Celeste
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.