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.

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

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.


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

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

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

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

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 

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.


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

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

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

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


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

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


0
 /*To get duplicate data in table */

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

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


-2

Experimentar

SELECT orgName, id, count(*) as dupes
FROM organizations
GROUP BY orgName, id
HAVING 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.