Localizando valores duplicados em uma tabela SQL

1935

É fácil encontrar duplicatas com um campo:

SELECT name, COUNT(email) 
FROM users
GROUP BY email
HAVING COUNT(email) > 1

Então, se tivermos uma mesa

ID   NAME   EMAIL
1    John   [email protected]
2    Sam    [email protected]
3    Tom    [email protected]
4    Bob    [email protected]
5    Tom    [email protected]

Essa consulta nos dará John, Sam, Tom, Tom, porque todos eles têm o mesmo email .

No entanto, o que eu quero é obter duplicatas com o mesmo email e name .

Ou seja, eu quero pegar "Tom", "Tom".

A razão pela qual eu preciso disso: cometi um erro e permiti inserir valores namee duplicados email. Agora eu preciso remover / alterar as duplicatas, então preciso encontrá- las primeiro.

Alex
fonte
28
Eu não acho que permitiria que você selecione o nome no seu primeiro exemplo, pois ele não está em uma função agregada. "O que é a contagem de correspondência de endereços de email e seu nome" é um pouco de lógica complicado ...
sXe
3
Constatou que isso não funciona com o servidor MSSQL devido ao namecampo no SELECT.
E. van Putten
o que eu preciso é a identificação de registros com e
Marcos Di Paolo

Respostas:

3037
SELECT
    name, email, COUNT(*)
FROM
    users
GROUP BY
    name, email
HAVING 
    COUNT(*) > 1

Simplesmente agrupe nas duas colunas.

Nota: o padrão ANSI mais antigo é ter todas as colunas não agregadas no GROUP BY, mas isso mudou com a idéia de "dependência funcional" :

Na teoria do banco de dados relacional, uma dependência funcional é uma restrição entre dois conjuntos de atributos em uma relação de um banco de dados. Em outras palavras, dependência funcional é uma restrição que descreve o relacionamento entre atributos em um relacionamento.

O suporte não é consistente:

gbn
fonte
92
@webXL ONDE trabalha com recordes TENDO trabalhos individuais com o grupo
bjan
8
@gbn É possível incluir o ID nos resultados? Então seria mais fácil excluir essas duplicatas posteriormente.
user797717
13
@ user797717: você precisa ter MIN (ID) e exclua para valores de ID não nos últimos se MIN valores (ID)
GBN
1
E os casos em que qualquer uma das colunas tem valores nulos?
Ankit Dhingra
1
Agradecemos muito por isso, e sim ele faz o trabalho na Oracle, embora eu precisava singularidade da condição, então ao invés de>1 =1
Bill Naylor
370

tente isto:

declare @YourTable table (id int, name varchar(10), email varchar(50))

INSERT @YourTable VALUES (1,'John','John-email')
INSERT @YourTable VALUES (2,'John','John-email')
INSERT @YourTable VALUES (3,'fred','John-email')
INSERT @YourTable VALUES (4,'fred','fred-email')
INSERT @YourTable VALUES (5,'sam','sam-email')
INSERT @YourTable VALUES (6,'sam','sam-email')

SELECT
    name,email, COUNT(*) AS CountOf
    FROM @YourTable
    GROUP BY name,email
    HAVING COUNT(*)>1

RESULTADO:

name       email       CountOf
---------- ----------- -----------
John       John-email  2
sam        sam-email   2

(2 row(s) affected)

se você quiser os IDs dos dups, use isto:

SELECT
    y.id,y.name,y.email
    FROM @YourTable y
        INNER JOIN (SELECT
                        name,email, COUNT(*) AS CountOf
                        FROM @YourTable
                        GROUP BY name,email
                        HAVING COUNT(*)>1
                    ) dt ON y.name=dt.name AND y.email=dt.email

RESULTADO:

id          name       email
----------- ---------- ------------
1           John       John-email
2           John       John-email
5           sam        sam-email
6           sam        sam-email

(4 row(s) affected)

para excluir as duplicatas, tente:

DELETE d
    FROM @YourTable d
        INNER JOIN (SELECT
                        y.id,y.name,y.email,ROW_NUMBER() OVER(PARTITION BY y.name,y.email ORDER BY y.name,y.email,y.id) AS RowRank
                        FROM @YourTable y
                            INNER JOIN (SELECT
                                            name,email, COUNT(*) AS CountOf
                                            FROM @YourTable
                                            GROUP BY name,email
                                            HAVING COUNT(*)>1
                                        ) dt ON y.name=dt.name AND y.email=dt.email
                   ) dt2 ON d.id=dt2.id
        WHERE dt2.RowRank!=1
SELECT * FROM @YourTable

RESULTADO:

id          name       email
----------- ---------- --------------
1           John       John-email
3           fred       John-email
4           fred       fred-email
5           sam        sam-email

(4 row(s) affected)
KM.
fonte
127

Tente o seguinte:

SELECT name, email
FROM users
GROUP BY name, email
HAVING ( COUNT(*) > 1 )
Chris Van Opstal
fonte
72

Se você deseja excluir as duplicatas, eis uma maneira muito mais simples de fazer isso do que encontrar linhas pares / ímpares em uma sub-seleção tripla:

SELECT id, name, email 
FROM users u, users u2
WHERE u.name = u2.name AND u.email = u2.email AND u.id > u2.id

E assim, para excluir:

DELETE FROM users
WHERE id IN (
    SELECT id/*, name, email*/
    FROM users u, users u2
    WHERE u.name = u2.name AND u.email = u2.email AND u.id > u2.id
)

Muito mais fácil de ler e entender IMHO

Nota: O único problema é que você deve executar a solicitação até que não haja linhas excluídas, pois você exclui apenas 1 de cada duplicado a cada vez

AncAinu
fonte
2
Agradável e fácil de ler; Eu gostaria de encontrar uma maneira de excluir várias linhas duplicadas de uma só vez.
9788 Dickon Reed
1
Isso não funciona para mim como eu ficarYou can't specify target table 'users' for update in FROM clause
WhiteCat
1
@Whitecat parece um problema simples do MySQL: stackoverflow.com/questions/4429319/…
AncAinu
1
Falha por mim. Eu obtenho: "DBD :: CSV :: st execute falhou: O uso do valor não inicializado $ _ [1] no elemento hash em /Users/hornenj/perl5/perlbrew/perls/perl-5.26.0/lib/site_perl/5.26. 0 / SQL / Eval.pm linha 43 "
Nigel Horne
1
Eu acho que a cláusula where deve ser "u.name = u2.name AND u.email = u2.email AND (u.id> u2.id OU u2.id> u.id)", não é?
GiveEmTheBoot 18/02/19
48

Tente o seguinte:

SELECT * FROM
(
    SELECT Id, Name, Age, Comments, Row_Number() OVER(PARTITION BY Name, Age ORDER By Name)
        AS Rank 
        FROM Customers
) AS B WHERE Rank>1
gaurav singh
fonte
3
Uma pequena alteração no SELECT * me ajudou a solucionar uma pesquisa de uma hora. Eu nunca usei o OVER (PARTITION BY antes de eu nunca deixam de se surpreender com quantas maneiras de fazer a mesma coisa em SQL.!
Joe Ruder
33
 SELECT name, email 
    FROM users
    WHERE email in
    (SELECT email FROM users
    GROUP BY email 
    HAVING COUNT(*)>1)
PRADEEPTA VIRLLEY
fonte
28

Um pouco atrasado para a festa, mas achei uma solução muito legal para encontrar todos os IDs duplicados:

SELECT GROUP_CONCAT( id )
FROM users
GROUP BY email
HAVING ( COUNT(email) > 1 )
Indivision Dev
fonte
2
Parece ser uma solução sintática para o açúcar. Bom achado.
Chef_Code
3
Lembre-se de que ele GROUP_CONCATserá interrompido após um período predeterminado, portanto você pode não obter todos os ids.
v010dya
24

tente este código

WITH CTE AS

( SELECT Id, Name, Age, Comments, RN = ROW_NUMBER()OVER(PARTITION BY Name,Age ORDER BY ccn)
FROM ccnmaster )
select * from CTE 
Tanmay Nehete
fonte
23

Isso seleciona / exclui todos os registros duplicados, exceto um registro de cada grupo de duplicados. Portanto, a exclusão deixa todos os registros exclusivos + um registro de cada grupo de duplicatas.

Selecione duplicatas:

SELECT *
FROM table
WHERE
    id NOT IN (
        SELECT MIN(id)
        FROM table
        GROUP BY column1, column2
);

Excluir duplicatas:

DELETE FROM table
WHERE
    id NOT IN (
        SELECT MIN(id)
        FROM table
        GROUP BY column1, column2
);

Esteja ciente de quantidades maiores de registros, pois isso pode causar problemas de desempenho.

Martin Silovský
fonte
2
Erro na consulta de exclusão - Você não pode especificar 'cidades' da tabela de destino para atualização na cláusula FROM
Ali Azhar
2
Não há tabela 'cidades' nem cláusula de atualização. O que você quer dizer? Onde está um erro na consulta de exclusão?
Martin Silovský
2
Como isso funciona com os dados do OP?
thoroc
3
O que significa o "OP"?
Martin Silovský
19

Caso você trabalhe com a Oracle, é preferível:

create table my_users(id number, name varchar2(100), email varchar2(100));

insert into my_users values (1, 'John', '[email protected]');
insert into my_users values (2, 'Sam', '[email protected]');
insert into my_users values (3, 'Tom', '[email protected]');
insert into my_users values (4, 'Bob', '[email protected]');
insert into my_users values (5, 'Tom', '[email protected]');

commit;

select *
  from my_users
 where rowid not in (select min(rowid) from my_users group by name, email);
xDBA
fonte
15
select name, email
, case 
when ROW_NUMBER () over (partition by name, email order by name) > 1 then 'Yes'
else 'No'
end "duplicated ?"
from users
Narendra
fonte
2
As respostas apenas de código são desaprovadas no Stack Overflow. Você poderia explicar por que isso responde à pergunta?
Rich Benner
2
@ RichBenner: Eu não encontrei a resposta, como, cada linha no resultado e que nos diz quais são linhas duplicadas e quais não estão de uma só vez e que não devem ser agrupadas, porque se queremos combinar isso consultar com qualquer outro grupo de consultas por não é uma boa opção.
Narendra
2
Ao adicionar o ID à instrução select e filtrar os duplicados, é possível excluir os IDs duplicados e continuar com cada um deles.
Antoine Reinhold Bertrand
12

Se você deseja ver se há alguma linha duplicada na sua tabela, usei abaixo a Consulta:

create table my_table(id int, name varchar(100), email varchar(100));

insert into my_table values (1, 'shekh', '[email protected]');
insert into my_table values (1, 'shekh', '[email protected]');
insert into my_table values (2, 'Aman', '[email protected]');
insert into my_table values (3, 'Tom', '[email protected]');
insert into my_table values (4, 'Raj', '[email protected]');


Select COUNT(1) As Total_Rows from my_table 
Select Count(1) As Distinct_Rows from ( Select Distinct * from my_table) abc 
shekhar Kumar
fonte
11

Essa é a coisa mais fácil que eu inventei. Ele usa uma expressão de tabela comum (CTE) e uma janela de partição (acho que esses recursos estão no SQL 2008 e posterior).

Este exemplo localiza todos os alunos com nome e data duplicados. Os campos que você deseja verificar a duplicação estão na cláusula OVER. Você pode incluir outros campos que desejar na projeção.

with cte (StudentId, Fname, LName, DOB, RowCnt)
as (
SELECT StudentId, FirstName, LastName, DateOfBirth as DOB, SUM(1) OVER (Partition By FirstName, LastName, DateOfBirth) as RowCnt
FROM tblStudent
)
SELECT * from CTE where RowCnt > 1
ORDER BY DOB, LName
Darrel Lee
fonte
11
select id,name,COUNT(*) from user group by Id,Name having COUNT(*)>1
Debendra Dash
fonte
10

Como podemos contar os valores duplicados ?? ou é repetido 2 vezes ou mais que 2. apenas conte-os, não em grupo.

tão simples quanto

select COUNT(distinct col_01) from Table_01
Muhammad Tahir
fonte
2
Como isso funcionaria para a pergunta feita? Isso não fornece linhas que duplicam informações em várias colunas (por exemplo, "email" e "nome") em linhas diferentes.
Jeroen
10

Usando CTE também podemos encontrar valores duplicados como este

with MyCTE
as
(
select Name,EmailId,ROW_NUMBER() over(PARTITION BY EmailId order by id) as Duplicate from [Employees]

)
select * from MyCTE where Duplicate>1
Debendra Dash
fonte
9
 select emp.ename, emp.empno, dept.loc 
          from emp
 inner join dept 
          on dept.deptno=emp.deptno
 inner join
    (select ename, count(*) from
    emp
    group by ename, deptno
    having count(*) > 1)
 t on emp.ename=t.ename order by emp.ename
/
nave
fonte
8

SELECT id, COUNT(id) FROM table1 GROUP BY id HAVING COUNT(id)>1;

Eu acho que isso funcionará corretamente para pesquisar valores repetidos em uma coluna específica.

user4877838
fonte
6
Isso não adiciona nada à resposta principal e, tecnicamente, nem difere realmente do código OP publicado na pergunta.
Jeroen
7
SELECT * FROM users u where rowid = (select max(rowid) from users u1 where
u.email=u1.email);
Panky031
fonte
6

Isso também deve funcionar, talvez tente.

  Select * from Users a
            where EXISTS (Select * from Users b 
                where (     a.name = b.name 
                        OR  a.email = b.email)
                     and a.ID != b.id)

Especialmente bom no seu caso Se você procurar duplicatas que tenham algum tipo de prefixo ou alteração geral, como, por exemplo, um novo domínio no correio. então você pode usar replace () nessas colunas

veritaS
fonte
5

Se você deseja encontrar dados duplicados (por um ou vários critérios) e selecione as linhas reais.

with MYCTE as (
    SELECT DuplicateKey1
        ,DuplicateKey2 --optional
        ,count(*) X
    FROM MyTable
    group by DuplicateKey1, DuplicateKey2
    having count(*) > 1
) 
SELECT E.*
FROM MyTable E
JOIN MYCTE cte
ON E.DuplicateKey1=cte.DuplicateKey1
    AND E.DuplicateKey2=cte.DuplicateKey2
ORDER BY E.DuplicateKey1, E.DuplicateKey2, CreatedAt

http://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/

Lauri Lubi
fonte
4
SELECT name, email,COUNT(email) 
FROM users 
WHERE email IN (
    SELECT email 
    FROM users 
    GROUP BY email 
    HAVING COUNT(email) > 1)
Mohammad Neamul Islam
fonte
Você não pode usar COUNTsem GROUP BY, a menos que se refira à tabela inteira.
RalfFriedl
Sem grupo Por você que usou COUNT, mas aqui estou
cometendo
3

Para excluir registros cujos nomes são duplicados

;WITH CTE AS    
(

    SELECT ROW_NUMBER() OVER (PARTITION BY name ORDER BY name) AS T FROM     @YourTable    
)

DELETE FROM CTE WHERE T > 1
Xerife
fonte
3

Para verificar do registro duplicado em uma tabela.

select * from users s 
where rowid < any 
(select rowid from users k where s.name = k.name and s.email = k.email);

ou

select * from users s 
where rowid not in 
(select max(rowid) from users k where s.name = k.name and s.email = k.email);

Para excluir o registro duplicado em uma tabela.

delete from users s 
where rowid < any 
(select rowid from users k where s.name = k.name and s.email = k.email);

ou

delete from users s 
where rowid not in 
(select max(rowid) from users k where s.name = k.name and s.email = k.email);
Arun Salomão
fonte
1

SELECT column_name,COUNT(*) FROM TABLE_NAME GROUP BY column1, HAVING COUNT(*) > 1;

rahul kumar
fonte
1

Podemos usar aqui que trabalham em funções agregadas, como mostrado abaixo

create table #TableB (id_account int, data int, [date] date)
insert into #TableB values (1 ,-50, '10/20/2018'),
(1, 20, '10/09/2018'),
(2 ,-900, '10/01/2018'),
(1 ,20, '09/25/2018'),
(1 ,-100, '08/01/2018')  

SELECT id_account , data, COUNT(*)
FROM #TableB
GROUP BY id_account , data
HAVING COUNT(id_account) > 1

drop table #TableB

Aqui, como dois campos, id_account e dados são usados ​​com Count (*). Portanto, ele fornecerá todos os registros com mais de uma vez os mesmos valores nas duas colunas.

Por alguma razão, erroneamente, tínhamos perdido a adição de restrições na tabela do servidor SQL e os registros foram inseridos duplicados em todas as colunas com o aplicativo front-end. Em seguida, podemos usar a consulta abaixo para excluir a consulta duplicada da tabela.

SELECT DISTINCT * INTO #TemNewTable FROM #OriginalTable
TRUNCATE TABLE #OriginalTable
INSERT INTO #OriginalTable SELECT * FROM #TemNewTable
DROP TABLE #TemNewTable

Aqui, pegamos todos os registros distintos da tabela original e excluímos os registros da tabela original. Novamente, inserimos todos os valores distintos da nova tabela na tabela original e excluímos a nova tabela.

Suraj Kumar
fonte
1

Você pode tentar isso

SELECT NAME, EMAIL, COUNT(*)
FROM USERS
GROUP BY 1,2
HAVING COUNT(*) > 1
adesh
fonte
1

A coisa mais importante aqui é ter a função mais rápida. Também índices de duplicatas devem ser identificados. A junção automática é uma boa opção, mas para ter uma função mais rápida, é melhor encontrar primeiro as linhas que têm duplicatas e depois juntar-se à tabela original para encontrar a identificação das linhas duplicadas. Finalmente, encomende por qualquer coluna, exceto a id, para ter linhas duplicadas próximas uma da outra.

SELECT u.*
FROM users AS u
JOIN (SELECT username, email
      FROM users
      GROUP BY username, email
      HAVING COUNT(*)>1) AS w
ON u.username=w.username AND u.email=w.email
ORDER BY u.email;
RyanAbnavi
fonte
0

Você pode usar a palavra-chave SELECT DISTINCT para se livrar das duplicatas. Você também pode filtrar por nome e colocar todos com esse nome em uma tabela.

Parkofadown
fonte
0

O código exato pode variar dependendo se você deseja encontrar linhas duplicadas ou apenas IDs diferentes com o mesmo email e nome. Se id for uma chave primária ou tiver uma restrição exclusiva, essa distinção não existe, mas a pergunta não especifica isso. No primeiro caso, você pode usar o código fornecido em várias outras respostas:

SELECT name, email, COUNT(*)
FROM users
GROUP BY name, email
HAVING COUNT(*) > 1

No último caso, você usaria:

SELECT name, email, COUNT(DISTINCT id)
FROM users
GROUP BY name, email
HAVING COUNT(DISTINCT id) > 1
ORDER BY COUNT(DISTINCT id) DESC
RET
fonte