Localizando linhas duplicadas no SQL Server

231

Eu tenho um banco de dados de organizações do SQL Server e há muitas linhas duplicadas. Desejo executar uma instrução select para capturar tudo isso e a quantidade de enganos, mas também retornar os IDs associados a cada organização.

Uma declaração como:

SELECT     orgName, COUNT(*) AS dupes  
FROM         organizations  
GROUP BY orgName  
HAVING      (COUNT(*) > 1)

Retornará algo como

orgName        | dupes  
ABC Corp       | 7  
Foo Federation | 5  
Widget Company | 2 

Mas eu também gostaria de pegar os IDs deles. Há alguma maneira de fazer isso? Talvez como um

orgName        | dupeCount | id  
ABC Corp       | 1         | 34  
ABC Corp       | 2         | 5  
...  
Widget Company | 1         | 10  
Widget Company | 2         | 2  

O motivo é que também há uma tabela separada de usuários vinculados a essas organizações, e eu gostaria de unificá-los (portanto, remova os dupes para que os usuários se vinculem à mesma organização em vez de dupe orgs). Mas gostaria de fazer parte manualmente para não estragar nada, mas ainda assim precisaria de uma declaração retornando os IDs de todas as organizações duplas para que eu possa percorrer a lista de usuários.

xtine
fonte

Respostas:

313
select o.orgName, oc.dupeCount, o.id
from organizations o
inner join (
    SELECT orgName, COUNT(*) AS dupeCount
    FROM organizations
    GROUP BY orgName
    HAVING COUNT(*) > 1
) oc on o.orgName = oc.orgName
RedFilter
fonte
4
existem limitações nesta consulta, por exemplo, se o número de registros é de 10 milhões?
a vapor
3
@Steam Você está correto: esta resposta não é eficiente em um banco de dados maior com milhões de registros. Prefira a resposta GroupBy / Having enviada por Aykut, que pode ser melhor otimizada pelo banco de dados. Uma exceção: sugiro usar Count (0) em vez de Count (*), para simplificar as coisas.
Mike Christian
1
@ Mike - por que Count (0) vs Count (*)?
KornMuffin
2
@KornMuffin Em retrospecto, meu comentário sobre Count () é nulo. O uso de uma avaliação não nula em Count () é útil apenas quando você deseja contar resultados não nulos retornados por uma associação externa. Caso contrário, use Contagem (*). Uma ótima explicação é encontrada aqui .
Mike Christian
usar isnull()para colunas anuláveis na onseção
Arif Ulusoy
92

Você pode executar a seguinte consulta e encontrar as duplicatas com max(id)e excluir essas linhas.

SELECT orgName, COUNT(*), Max(ID) AS dupes 
FROM organizations 
GROUP BY orgName 
HAVING (COUNT(*) > 1)

Mas você precisará executar essa consulta algumas vezes.

Aykut Akıncı
fonte
Você precisa executá-lo exatamente MAX( COUNT(*) ) - 1vezes, o que ainda pode ser possível.
DerMike 9/08/16
1
oi é a sua maneira de obter todos os id em vez de max id como para 2 eu posso usar max e min, mas e quanto a mais de 2? @DerMike
Mukherjee
31

Você pode fazer assim:

SELECT
    o.id, o.orgName, d.intCount
FROM (
     SELECT orgName, COUNT(*) as intCount
     FROM organizations
     GROUP BY orgName
     HAVING COUNT(*) > 1
) AS d
    INNER JOIN organizations o ON o.orgName = d.orgName

Se você deseja retornar apenas os registros que podem ser excluídos (deixando um de cada), você pode usar:

SELECT
    id, orgName
FROM (
     SELECT 
         orgName, id,
         ROW_NUMBER() OVER (PARTITION BY orgName ORDER BY id) AS intRow
     FROM organizations
) AS d
WHERE intRow != 1

Editar: o SQL Server 2000 não possui a função ROW_NUMBER (). Em vez disso, você pode usar:

SELECT
    o.id, o.orgName, d.intCount
FROM (
     SELECT orgName, COUNT(*) as intCount, MIN(id) AS minId
     FROM organizations
     GROUP BY orgName
     HAVING COUNT(*) > 1
) AS d
    INNER JOIN organizations o ON o.orgName = d.orgName
WHERE d.minId != o.id
Paulo
fonte
A primeira afirmação funciona, mas a segunda não parece funcionar.
Xtine
O SQL Server parece não conseguir reconhecer o número da linha ()?
xtine
Ah ... você tem uma versão mais antiga do SQL Server? Eu acredito que foi introduzido no SQL Server 2005.
Paul
3
obrigado novamente, cada vez que eu preciso fazer isso eu chegar aqui e te amo
workabyte
9

A solução marcada como correta não funcionou para mim, mas achei esta resposta que funcionou muito bem: Obtenha uma lista de linhas duplicadas no MySql

SELECT n1.* 
FROM myTable n1
INNER JOIN myTable n2 
ON n2.repeatedCol = n1.repeatedCol
WHERE n1.id <> n2.id
ecairol
fonte
Você terá muitos truques no conjunto de resultados, portanto precisará lidar com eles também.
Renan
1
Se o ID for numérico, a verificação n1.id > n2.idimpedirá que cada par seja exibido duas vezes.
starwed 7/06/16
9

Você pode tentar isso, é melhor para você

 WITH CTE AS
    (
    SELECT *,RN=ROW_NUMBER() OVER (PARTITION BY orgName ORDER BY orgName DESC) FROM organizations 
    )
    select * from CTE where RN>1
    go
salvar código
fonte
alguma maneira de obter toda a id em dividir vírgula ou colunas diferentes
Arijit Mukherjee
6

Se você deseja excluir duplicatas:

WITH CTE AS(
   SELECT orgName,id,
       RN = ROW_NUMBER()OVER(PARTITION BY orgName ORDER BY Id)
   FROM organizations
)
DELETE FROM CTE WHERE RN > 1
akd
fonte
6
select * from [Employees]

Para encontrar o Registro 1 duplicado) Usando CTE

with mycte
as
(
select Name,EmailId,ROW_NUMBER() over(partition by Name,EmailId order by id) as Duplicate from [Employees]
)
select * from mycte

2) Usando GroupBy

select Name,EmailId,COUNT(name) as Duplicate from  [Employees] group by Name,EmailId 
Debendra Dash
fonte
Essa é a solução mais rápida aqui, ao selecionar dados com mais de 10 milhões de linhas. Graças
Fandango68
4
Select * from (Select orgName,id,
ROW_NUMBER() OVER(Partition By OrgName ORDER by id DESC) Rownum
From organizations )tbl Where Rownum>1

Portanto, os registros com rowum> 1 serão os registros duplicados na sua tabela. Primeiro, agrupe pelos registros e os serialize, dando-lhes os números de série. Portanto, rownum> 1 serão os registros duplicados que podem ser excluídos como tais.

Mike Clark
fonte
Eu gosto deste porque permite adicionar facilmente mais colunas na cláusula de seleção interna. Portanto, se você quiser retornar outras colunas da tabela 'Organizações', não precisará fazer um 'agrupar por' nessas colunas.
Gwasshoppa
2
select column_name, count(column_name)
from table_name
group by column_name
having count (column_name) > 1;

Src: https://stackoverflow.com/a/59242/1465252

iCrazybest
fonte
Isso funcionará apenas em tabelas que possuem uma única coluna. Que é provavelmente não é útil
Zach Smith
2
select a.orgName,b.duplicate, a.id
from organizations a
inner join (
    SELECT orgName, COUNT(*) AS duplicate
    FROM organizations
    GROUP BY orgName
    HAVING COUNT(*) > 1
) b on o.orgName = oc.orgName
group by a.orgName,a.id
user5336758
fonte
1
select orgname, count(*) as dupes, id 
from organizations
where orgname in (
    select orgname
    from organizations
    group by orgname
    having (count(*) > 1)
)
group by orgname, id
Jordão
fonte
1

Você tem várias maneiras de selecionar duplicate rows.

para minhas soluções, primeiro considere esta tabela, por exemplo

CREATE TABLE #Employee
(
ID          INT,
FIRST_NAME  NVARCHAR(100),
LAST_NAME   NVARCHAR(300)
)

INSERT INTO #Employee VALUES ( 1, 'Ardalan', 'Shahgholi' );
INSERT INTO #Employee VALUES ( 2, 'name1', 'lname1' );
INSERT INTO #Employee VALUES ( 3, 'name2', 'lname2' );
INSERT INTO #Employee VALUES ( 2, 'name1', 'lname1' );
INSERT INTO #Employee VALUES ( 3, 'name2', 'lname2' );
INSERT INTO #Employee VALUES ( 4, 'name3', 'lname3' );

Primeira solução:

SELECT DISTINCT *
FROM   #Employee;

WITH #DeleteEmployee AS (
                     SELECT ROW_NUMBER()
                            OVER(PARTITION BY ID, First_Name, Last_Name ORDER BY ID) AS
                            RNUM
                     FROM   #Employee
                 )

SELECT *
FROM   #DeleteEmployee
WHERE  RNUM > 1

SELECT DISTINCT *
FROM   #Employee

Solução completa: identitycampo de uso

SELECT DISTINCT *
FROM   #Employee;

ALTER TABLE #Employee ADD UNIQ_ID INT IDENTITY(1, 1)

SELECT *
FROM   #Employee
WHERE  UNIQ_ID < (
    SELECT MAX(UNIQ_ID)
    FROM   #Employee a2
    WHERE  #Employee.ID = a2.ID
           AND #Employee.FIRST_NAME = a2.FIRST_NAME
           AND #Employee.LAST_NAME = a2.LAST_NAME
)

ALTER TABLE #Employee DROP COLUMN UNIQ_ID

SELECT DISTINCT *
FROM   #Employee

e final de toda solução, use este comando

DROP TABLE #Employee
Ardalan Shahgholi
fonte
0

Eu acho que sei o que você precisa, eu precisava misturar as respostas e acho que consegui a solução que ele queria:

select o.id,o.orgName, oc.dupeCount, oc.id,oc.orgName
from organizations o
inner join (
    SELECT MAX(id) as id, orgName, COUNT(*) AS dupeCount
    FROM organizations
    GROUP BY orgName
    HAVING COUNT(*) > 1
) oc on o.orgName = oc.orgName

ter o ID máximo fornecerá o ID do publicador e o do original, que ele solicitou:

id org name , dublicate count (missing out in this case) 
id doublicate org name , doub count (missing out again because does not help in this case)

única coisa triste que você colocá-lo nesta forma

id , name , dubid , name

espero que ainda ajude

Arthur Kielbasa
fonte
0

Suponha que tenhamos a tabela 'Student' com 2 colunas:

  • student_id int
  • student_name varchar

    Records:
    +------------+---------------------+
    | student_id | student_name        |
    +------------+---------------------+
    |        101 | usman               |
    |        101 | usman               |
    |        101 | usman               |
    |        102 | usmanyaqoob         |
    |        103 | muhammadusmanyaqoob |
    |        103 | muhammadusmanyaqoob |
    +------------+---------------------+

Agora queremos ver registros duplicados Use esta consulta:

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
fonte
0

Eu tenho uma opção melhor para obter os registros duplicados em uma tabela

SELECT x.studid, y.stdname, y.dupecount
FROM student AS x INNER JOIN
(SELECT a.stdname, COUNT(*) AS dupecount
FROM student AS a INNER JOIN
studmisc AS b ON a.studid = b.studid
WHERE (a.studid LIKE '2018%') AND (b.studstatus = 4)
GROUP BY a.stdname
HAVING (COUNT(*) > 1)) AS y ON x.stdname = y.stdname INNER JOIN
studmisc AS z ON x.studid = z.studid
WHERE (x.studid LIKE '2018%') AND (z.studstatus = 4)
ORDER BY x.stdname

O resultado da consulta acima mostra todos os nomes duplicados com IDs de alunos exclusivos e número de ocorrências duplicadas

Clique aqui para ver o resultado do sql

SoftIdea
fonte
0
 /*To get duplicate data in table */

 SELECT COUNT(EmpCode),EmpCode FROM tbl_Employees WHERE Status=1 
  GROUP BY EmpCode HAVING COUNT(EmpCode) > 1
JIYAUL MUSTAPHA
fonte
0

Eu uso dois métodos para encontrar linhas duplicadas. O primeiro método é o mais famoso usando grupo por e tendo. O segundo método está usando CTE - Common Table Expression .

Conforme mencionado pelo @RedFilter, esse caminho também é correto. Muitas vezes acho que o método CTE também é útil para mim.

WITH TempOrg (orgName,RepeatCount)
AS
(
SELECT orgName,ROW_NUMBER() OVER(PARTITION by orgName ORDER BY orgName) 
AS RepeatCount
FROM dbo.organizations
)
select t.*,e.id from organizations   e
inner join TempOrg t on t.orgName= e.orgName
where t.RepeatCount>1

No exemplo acima, coletamos o resultado localizando ocorrência repetida usando ROW_NUMBER e PARTITION BY. Em seguida, aplicamos a cláusula where para selecionar apenas as linhas que estão na contagem de repetição superior a 1. Todo o resultado é coletado na tabela CTE e associado à tabela Organizações.

Fonte: CodoBee

Ishrar
fonte
-2

Experimentar

SELECT orgName, id, count(*) as dupes
FROM organizations
GROUP BY orgName, id
HAVING count(*) > 1;
ryan
fonte