Encontrando valores duplicados no MySQL


769

Eu tenho uma tabela com uma coluna varchar e gostaria de encontrar todos os registros que tenham valores duplicados nesta coluna. Qual é a melhor consulta que posso usar para encontrar as duplicatas?


1
Como você mencionou encontrar todos os registros, presumo que você precise conhecer as CHAVES e os valores duplicados nessa coluna varchar.
TechTravelThink 27/03/2009

Posso encontrar as chaves com bastante facilidade depois de obter os valores. Quero apenas uma lista de todos os valores duplicados.
31810 Jon Tackabury

Respostas:


1522

Faça um SELECTcom uma GROUP BYcláusula. Digamos que nome é a coluna na qual você deseja encontrar duplicatas:

SELECT name, COUNT(*) c FROM table GROUP BY name HAVING c > 1;

Isso retornará um resultado com o valor do nome na primeira coluna e uma contagem de quantas vezes esse valor aparece na segunda.


27
Mas como isso é útil se você não pode obter os IDs das linhas com valores duplicados? Sim, você pode fazer uma nova consulta correspondente a cada valor duplicado, mas é possível simplesmente listar as duplicatas?
22414 NobleUplift

23
@NobleUplift Você pode fazer um GROUP_CONCAT(id)e ele listará os IDs. Veja minha resposta para um exemplo.
Matt Rardon

5
O que significaria se dissesse ERROR: column "c" does not exist LINE 1?
Utilizador

15
Estou confuso por que essa é a resposta aceita e por que tem tantos votos positivos. O OP perguntou: "Gostaria de encontrar todos os registros que tenham valores duplicados nesta coluna". Esta resposta retorna uma tabela de contagens. -1
Monica Heddneck 03/04

4
Para aqueles que não entendem como o HAVING funciona - é simplesmente um filtro no conjunto de resultados, o que acontece após a consulta principal.
John Hunt

236
SELECT varchar_col
FROM table
GROUP BY varchar_col
HAVING COUNT(*) > 1;

10
Superior à resposta do @ levik, pois não adiciona uma coluna extra. Torna útil para uso com IN()/ NOT IN().
wmassingham

172
SELECT  *
FROM    mytable mto
WHERE   EXISTS
        (
        SELECT  1
        FROM    mytable mti
        WHERE   mti.varchar_column = mto.varchar_column
        LIMIT 1, 1
        )

Essa consulta retorna registros completos, não apenas distintos varchar_column.

Esta consulta não usa COUNT(*). Se houver muitas duplicatas, COUNT(*)for caro e você não precisar do todo COUNT(*), precisará saber se há duas linhas com o mesmo valor.

Ter um índice ativado varchar_columnirá, obviamente, acelerar bastante essa consulta.


3
Muito bom. Eu adicionei ORDER BY varchar_column DESCao final da consulta.
trante 28/05

8
Esta deve ser a resposta aceita, como GROUP BYe HAVINGretorna apenas uma das possíveis duplicatas. Além disso, desempenho com campo indexado em vez de COUNT(*)e a possibilidade ORDER BYde agrupar registros duplicados.
Rémi Breton

1
Conforme declarado nos comentários acima, esta consulta permite listar todas as linhas duplicadas. Muito útil.
TryHarder 26/08/16

4
Olhando para isso, não entendo como isso funcionaria. A condição interna sempre será verdadeira, pois qualquer linha da tabela externa também estará disponível na tabela interna e, portanto, cada linha sempre corresponderá pelo menos a si mesma? Eu tentei a consulta e obtive o resultado que eu suspeitava - todas as linhas retornavam. Mas com tantos votos positivos, estou duvidando de mim mesmo. Não está faltando na consulta interna algo como "AND mto.id <> mti.id"? Funciona para mim quando adiciono isso.
CLOX

2
@Quassnoi Tudo bem. Eu tentei colocá-lo no sqlfiddle, mas desisti, pois todas as consultas que tento executar, além de criar o esquema, atingiram o tempo limite. Eu descobri que apenas remover "EXISTS" também faz a consulta funcionar corretamente para mim.
Clox

144

Com base na resposta da levik para obter os IDs das linhas duplicadas, você pode fazer isso GROUP_CONCATse o seu servidor suportar (isso retornará uma lista de IDs separados por vírgula).

SELECT GROUP_CONCAT(id), name, COUNT(*) c FROM documents GROUP BY name HAVING c > 1;

12
Todo esse tempo sem saber sobre GROUP_CONCAT ()! muito muito útil
aesede

Realmente apreciado Matt. Isso é realmente útil! Para aqueles que tentam atualizar no phpmyadmin, se você deixar o id junto com a função assim: SELECT id, GROUP_CONCAT(id), name, COUNT(*) c [...]ele permite a edição em linha e deve atualizar todas as linhas envolvidas (ou pelo menos a primeira que corresponda), mas infelizmente a edição gera um erro de Javascript. ..
Armfoot

Como você calcularia quantos IDs estão sujeitos a duplicação?
precisa saber é o seguinte

2
Como não obtenho todos os IDs agrupados, mas listados do primeiro ao último; com todos os seus respectivos valores nas colunas ao lado deles? Portanto, em vez de agrupá-lo, ele mostra apenas o ID 1 e seu valor, o ID 2 e seu valor. MESMO se os valores para o ID forem os mesmos.
MailBlade 15/02

1
Resposta extremamente útil, essa deve ser a melhor opção para que mais pessoas a vejam. Lembro-me da quantidade de dor que passei ao criar essas listas, e estava disponível o tempo todo como comando. #
John John

13

Supondo que sua tabela se chame TableABC e a coluna que você deseja seja Col e a chave primária para T1 seja Key.

SELECT a.Key, b.Key, a.Col 
FROM TableABC a, TableABC b
WHERE a.Col = b.Col 
AND a.Key <> b.Key

A vantagem dessa abordagem sobre a resposta acima é que ela fornece a chave.


4
+1 Porque é útil. Embora, ironicamente, o próprio resultado contém duplicatas (ele lista a e b, então b e a.)
Fabien Snauwaert

2
@FabienSnauwaert Você pode se livrar de algumas das duplicatas comparando inferior a (ou superior)
Michael

@TechTravelThink sua resposta é muito clara, obrigado por isso, mas em uma tabela grande leva algum tempo (cerca de 2 min em mais 20.000 tabelas de entradas) e depois de mostrar 25 primeiros resultados, se eu clicar para mostrar o próximo, phpmyadmin show error "# 1052 - A coluna 'id' na cláusula de ordem é ambígua "
bcag2

12
SELECT * 
FROM `dps` 
WHERE pid IN (SELECT pid FROM `dps` GROUP BY pid HAVING COUNT(pid)>1)

1
Não, porque esse é possivelmente o mais lento do lote. As subseleções são notoriamente lentas, pois são executadas para cada linha retornada.
Oddman

10

Para descobrir quantos registros estão duplicados na coluna de nome em Funcionário, a consulta abaixo é útil;

Select name from employee group by name having count(*)>1;

10

para obter todos os dados que contêm duplicação, usei isso:

SELECT * FROM TableName INNER JOIN(
  SELECT DupliactedData FROM TableName GROUP BY DupliactedData HAVING COUNT(DupliactedData) > 1 order by DupliactedData)
  temp ON TableName.DupliactedData = temp.DupliactedData;

TableName = a tabela com a qual você está trabalhando.

DupliactedData = os dados duplicados que você está procurando.


Este mostra cada duplicado em sua própria linha. Isso é o que eu preciso. Obrigado.
warmwhisky

8

Minha consulta final incorporou algumas das respostas aqui que ajudaram - combinando agrupar por, contar & GROUP_CONCAT.

SELECT GROUP_CONCAT(id), `magento_simple`, COUNT(*) c 
FROM product_variant 
GROUP BY `magento_simple` HAVING c > 1;

Isso fornece a identificação dos dois exemplos (separados por vírgula), o código de barras que eu precisava e quantas duplicatas.

Mude a tabela e as colunas adequadamente.


8

Não estou vendo nenhuma abordagem JOIN, que tem muitos usos em termos de duplicatas.

Essa abordagem fornece resultados dobrados reais.

SELECT t1.* FROM my_table as t1 
LEFT JOIN my_table as t2 
ON t1.name=t2.name and t1.id!=t2.id 
WHERE t2.id IS NOT NULL 
ORDER BY t1.name

2
FYI - Você desejará 'selecionar um somecol distinto ...' se houver a possibilidade de existir mais de 1 registro duplicado, caso contrário, os resultados conterão duplicatas das linhas duplicadas encontradas.
Tirou

7
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


7

Levando a resposta do @ maxyfc adiante, eu precisava encontrar todas as linhas retornadas com os valores duplicados, para poder editá-las no MySQL Workbench :

SELECT * FROM table
   WHERE field IN (
     SELECT field FROM table GROUP BY field HAVING count(*) > 1
   ) ORDER BY field

6

Vi o resultado acima e a consulta funcionará bem se você precisar verificar o valor da coluna única duplicada. Por exemplo, email.

Mas se você precisar verificar com mais colunas e desejar verificar a combinação do resultado, para que esta consulta funcione bem:

SELECT COUNT(CONCAT(name,email)) AS tot,
       name,
       email
FROM users
GROUP BY CONCAT(name,email)
HAVING tot>1 (This query will SHOW the USER list which ARE greater THAN 1
              AND also COUNT)

Exatamente o que era necessário! Aqui a minha consulta, verificando 3 campos para duplicatas:SELECT COUNT(CONCAT(userid,event,datetime)) AS total, userid, event, datetime FROM mytable GROUP BY CONCAT(userid, event, datetime ) HAVING total>1
Kai Noack

4

Eu prefiro usar funções em janelas (MySQL 8.0+) para encontrar duplicatas porque eu podia ver a linha inteira:

WITH cte AS (
  SELECT *
    ,COUNT(*) OVER(PARTITION BY col_name) AS num_of_duplicates_group
    ,ROW_NUMBER() OVER(PARTITION BY col_name ORDER BY col_name2) AS pos_in_group
  FROM table
)
SELECT *
FROM cte
WHERE num_of_duplicates_group > 1;

DB Fiddle Demo


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

1
Fazer a mesma subconsulta duas vezes parece ineficiente.
21414 NobleUplift


3
CREATE TABLE tbl_master
    (`id` int, `email` varchar(15));

INSERT INTO tbl_master
    (`id`, `email`) VALUES
    (1, 'test1@gmail.com'),
    (2, 'test2@gmail.com'),
    (3, 'test1@gmail.com'),
    (4, 'test2@gmail.com'),
    (5, 'test5@gmail.com');

QUERY : SELECT id, email FROM tbl_master
WHERE email IN (SELECT email FROM tbl_master GROUP BY email HAVING COUNT(id) > 1)

2
SELECT DISTINCT a.email FROM `users` a LEFT JOIN `users` b ON a.email = b.email WHERE a.id != b.id;

1
Vale ressaltar que isso é insuportavelmente lento ou pode até não terminar se a coluna que está sendo consultada não estiver indexada. Caso contrário, eu poderia mudar a.emailpara a.*e obter todos os IDs das linhas com duplicatas.
NobleUplift

@NobleUplift Do que você está falando?
Michael

@ Michael Bem, já que ele tem três anos, não posso testar a versão do MySQL que estava usando, mas tentei a mesma consulta em um banco de dados em que a coluna que selecionei não tinha um índice, por isso demorou bastante tempo. alguns segundos para terminar. Alterando para SELECT DISTINCT a.*resolvido quase instantaneamente.
NobleUplift

@NobleUplift Ah ok. Eu posso entender que é lento ... a parte que me preocupa é "talvez nem termine".
Michael

@ Michael Eu não lembro em qual tabela do nosso sistema eu tinha que executar essa consulta, mas para aqueles com alguns milhões de registros provavelmente teriam terminado, mas em um tempo que demorou tanto que eu desisti de ver quando na verdade terminaria.
NobleUplift

1

Para remover linhas duplicadas com vários campos, primeiro cancele-as na nova chave exclusiva especificada para as únicas linhas distintas e, em seguida, use o comando "agrupar por" para remover as linhas duplicadas com a mesma nova chave exclusiva:

Create TEMPORARY table tmp select concat(f1,f2) as cfs,t1.* from mytable as t1;
Create index x_tmp_cfs on tmp(cfs);
Create table unduptable select f1,f2,... from tmp group by cfs;

você também pode adicionar uma explicação?
Robert

Por que não usar CREATE TEMPORARY TABLE ...? Uma pequena explicação da sua solução seria ótima.
maxhb

1

Uma contribuição muito tardia ... no caso de ajudar alguém a passar o tempo ... eu tinha a tarefa de encontrar pares de transações correspondentes (na verdade, ambos os lados das transferências de conta para conta) em um aplicativo bancário, para identificar quais eram 'de' e 'para' para cada transação de transferência entre contas, então acabamos com isso:

SELECT 
    LEAST(primaryid, secondaryid) AS transactionid1,
    GREATEST(primaryid, secondaryid) AS transactionid2
FROM (
    SELECT table1.transactionid AS primaryid, 
        table2.transactionid AS secondaryid
    FROM financial_transactions table1
    INNER JOIN financial_transactions table2 
    ON table1.accountid = table2.accountid
    AND table1.transactionid <> table2.transactionid 
    AND table1.transactiondate = table2.transactiondate
    AND table1.sourceref = table2.destinationref
    AND table1.amount = (0 - table2.amount)
) AS DuplicateResultsTable
GROUP BY transactionid1
ORDER BY transactionid1;

O resultado é que ele DuplicateResultsTablefornece linhas contendo transações correspondentes (ou seja, duplicadas), mas também fornece os mesmos IDs de transação ao contrário na segunda vez em que corresponde ao mesmo par; portanto, o externo SELECTestá lá para agrupar pelo primeiro ID de transação, o que é feito usando LEASTe GREATESTpara garantir que as duas transações sejam sempre da mesma ordem nos resultados, o que a torna segura GROUPpela primeira, eliminando todas as correspondências duplicadas. Percorreu quase um milhão de registros e identificou mais de 12.000 partidas em menos de 2 segundos. É claro que o transactionid é o índice principal, o que realmente ajudou.


1
Select column_name, column_name1,column_name2, count(1) as temp from table_name group by column_name having temp > 1

1
SELECT ColumnA, COUNT( * )
FROM Table
GROUP BY ColumnA
HAVING COUNT( * ) > 1

3
Isso está incorreto, pois também encontra ocorrências exclusivas. 0 deve ser 1.
Kafoso

1

Se você deseja remover o uso duplicado DISTINCT

Caso contrário, use esta consulta:

SELECT users.*,COUNT(user_ID) as user FROM users GROUP BY user_name HAVING user > 1;


0

Tente usar esta consulta:

SELECT name, COUNT(*) value_count FROM company_master GROUP BY name HAVING value_count > 1;
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.