Ricerca di righe duplicate in SQL Server


231

Ho un database di organizzazioni di SQL Server e ci sono molte righe duplicate. Voglio eseguire un'istruzione select per afferrare tutti questi e la quantità di duplicati, ma anche restituire gli ID associati a ciascuna organizzazione.

Una dichiarazione come:

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

Restituirà qualcosa di simile

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

Ma vorrei anche afferrarne gli ID. C'è un modo per fare questo? Forse come a

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

Il motivo è che esiste anche una tabella separata di utenti che si collega a queste organizzazioni e vorrei unificarle (quindi rimuovere i duplicati in modo che gli utenti si colleghino alla stessa organizzazione anziché alle organizzazioni duplicate). Ma vorrei separarmi manualmente in modo da non rovinare nulla, ma avrei comunque bisogno di una dichiarazione che restituisca gli ID di tutte le organizzazioni duplicate in modo da poter passare attraverso l'elenco degli utenti.

Risposte:


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
ci sono delle limitazioni in questa query, ad esempio se il numero di record è 10 milioni più?
Steam,

3
@Steam Hai ragione: questa risposta non è efficiente in un database più grande con milioni di record. Preferire GroupBy / Avere risposta inviata da Aykut, che può essere ottimizzata meglio dal database. Un'eccezione: suggerisco di usare Count (0) invece di Count (*), per semplificare le cose.
Mike Christian,

1
@Mike - perché Count (0) vs Count (*)?
KornMuffin,

2
@KornMuffin In retrospettiva, il mio commento su Count () è nullo. L'uso di una valutazione non nulla in Count () è utile solo quando si desidera contare i risultati non nulli restituiti da un join esterno. Altrimenti, usa Count (*). Una grande spiegazione si trova qui .
Mike Christian,

utilizzare isnull()per colonne nullable sulla onsezione
Arif Ulusoy

92

È possibile eseguire la query seguente e trovare i duplicati con max(id)ed eliminare quelle righe.

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

Ma dovrai eseguire questa query alcune volte.


Devi eseguirlo esattamente MAX( COUNT(*) ) - 1volte, che potrebbe essere ancora fattibile.
DerMike,

1
ciao è il loro modo per ottenere tutti gli id ​​invece di max id come per 2 posso usare max e min ma che ne dici di più di 2? @DerMike
Arijit Mukherjee,

31

Puoi farlo in questo modo:

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 si desidera restituire solo i record che possono essere eliminati (lasciando uno di ciascuno), è possibile utilizzare:

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

Modifica: SQL Server 2000 non ha la funzione ROW_NUMBER (). Invece, puoi usare:

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

La prima affermazione funziona, ma la seconda sembra non funzionare.
xtine,

SQL Server non sembra essere in grado di riconoscere row_number ()?
xtine,

Ah ... hai una versione precedente di SQL Server? Credo che sia stato introdotto in SQL Server 2005.
Paul,

3
grazie ancora, ogni volta che
devo

9

La soluzione contrassegnata come corretta non ha funzionato per me, ma ho trovato questa risposta che ha funzionato perfettamente: Ottieni un elenco di righe duplicate in MySql

SELECT n1.* 
FROM myTable n1
INNER JOIN myTable n2 
ON n2.repeatedCol = n1.repeatedCol
WHERE n1.id <> n2.id

Otterrai molti duplicati nel set di risultati, quindi dovrai occupartene anche tu.
Renan,

1
Se l'id è numerico, il controllo n1.id > n2.idimpedirà che ogni coppia venga visualizzata due volte.
inamidato il

9

Puoi provarlo, è meglio per te

 WITH CTE AS
    (
    SELECT *,RN=ROW_NUMBER() OVER (PARTITION BY orgName ORDER BY orgName DESC) FROM organizations 
    )
    select * from CTE where RN>1
    go

in qualsiasi modo per ottenere tutto l'id in virgola divisa o colonne diverse
Arijit Mukherjee

6

Se si desidera eliminare i duplicati:

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]

Per trovare un duplicato del record 1) Utilizzo di 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) Utilizzando GroupBy

select Name,EmailId,COUNT(name) as Duplicate from  [Employees] group by Name,EmailId 

Questa è la soluzione più veloce qui, quando si selezionano dati su oltre 10m di righe. Grazie
Fandango68,

4
Select * from (Select orgName,id,
ROW_NUMBER() OVER(Partition By OrgName ORDER by id DESC) Rownum
From organizations )tbl Where Rownum>1

Quindi i record con rowum> 1 saranno i record duplicati nella tua tabella. 'Partiziona per' il primo gruppo dai record e quindi serializzarli dando loro numeri di serie. Quindi rownum> 1 saranno i record duplicati che potrebbero essere eliminati come tali.


Mi piace questo perché ti consente di aggiungere facilmente più colonne nella clausola di selezione interna. Pertanto, se si desidera restituire altre colonne dalla tabella "Organizzazioni", non è necessario eseguire un "raggruppamento per" su tali colonne.
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

Hai diversi modi per selezionare duplicate rows.

per le mie soluzioni, prima considera questa tabella per esempio

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' );

Prima soluzione:

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

Soluzione profonda: utilizzare il identitycampo

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 la fine di tutta la soluzione usa questo comando

DROP TABLE #Employee

0

penso di sapere di cosa hai bisogno, ho dovuto mescolare tra le risposte e penso di aver ottenuto la soluzione che voleva:

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

avere l'id massimo ti darà l'id del diario e quello dell'originale che è quello che ha chiesto:

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)

l'unica cosa triste è che lo fai uscire in questo modulo

id , name , dubid , name

spero che aiuti ancora


0

Supponiamo di avere una tabella "Studente" con 2 colonne:

  • student_id int
  • student_name varchar

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

Ora vogliamo vedere i record duplicati Usa questa 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

Ho un'opzione migliore per ottenere i record duplicati in una tabella

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

Il risultato della query sopra mostra tutti i nomi duplicati con ID studente univoci e numero di occorrenze duplicate

Clicca qui per vedere il risultato di 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

Uso due metodi per trovare righe duplicate. Il 1 ° metodo è il più famoso usando group by e have. Il secondo metodo utilizza CTE - Common Table Expression .

Come accennato da @RedFilter, anche questo è giusto. Molte volte trovo che il metodo CTE sia utile anche per me.

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

Nell'esempio sopra abbiamo raccolto il risultato trovando ripetizione ricorrendo usando ROW_NUMBER e PARTITION BY. Quindi abbiamo applicato la clausola where per selezionare solo le righe in ripetizione contano più di 1. Tutto il risultato viene raccolto nella tabella CTE e unito alla tabella Organizzazioni.

Fonte: CodoBee


-2

Provare

SELECT orgName, id, count(*) as dupes
FROM organizations
GROUP BY orgName, id
HAVING count(*) > 1;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.