Znajdowanie zduplikowanych wierszy w programie SQL Server

231

Mam bazę danych SQL Server organizacji i istnieje wiele zduplikowanych wierszy. Chcę uruchomić instrukcję select, aby pobrać wszystkie te i liczbę kopii, ale także zwrócić identyfikatory powiązane z każdą organizacją.

Oświadczenie takie jak:

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

Zwróci coś takiego

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

Ale chciałbym też zdobyć ich identyfikatory. Czy jest na to sposób? Może jak

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

Powodem jest to, że istnieje osobna tabela użytkowników, którzy prowadzą do tych organizacji, i chciałbym je ujednolicić (dlatego usuwaj duplikaty, aby użytkownicy linkowali do tej samej organizacji zamiast dupe org). Ale chciałbym rozdzielić ręcznie, aby niczego nie schrzanić, ale nadal potrzebowałbym instrukcji zwracającej identyfikatory wszystkich duplikowanych organizacji, aby móc przeglądać listę użytkowników.

Xtine
źródło

Odpowiedzi:

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
źródło
4
czy są jakieś ograniczenia w tym zapytaniu, na przykład jeśli liczba rekordów wynosi 10 milionów plus?
Steam
3
@Steam Masz rację: ta odpowiedź nie jest skuteczna w większej bazie danych z milionami rekordów. Preferuj GroupBy / Posiadanie odpowiedzi przesłanej przez Aykut, która może być lepiej zoptymalizowana przez bazę danych. Jeden wyjątek: sugeruję użycie Count (0) zamiast Count (*), aby uprościć rzeczy.
Mike Christian
1
@Mike - dlaczego Count (0) vs. Count (*)?
KornMuffin,
2
@KornMuffin Z perspektywy czasu mój komentarz do Count () jest nieważny. Użycie oceny innej niż null w Count () jest przydatne tylko wtedy, gdy chcesz policzyć wyniki inne niż null zwrócone przez sprzężenie zewnętrzne. W przeciwnym razie użyj Count (*). Świetne wyjaśnienie znajduje się tutaj .
Mike Christian,
użyj isnull()dla zerowalnych kolumn w onsekcji
Arif Ulusoy
92

Możesz uruchomić następujące zapytanie, znaleźć duplikaty max(id)i usunąć te wiersze.

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

Ale będziesz musiał uruchomić to zapytanie kilka razy.

Aykut Akıncı
źródło
Musisz uruchomić go dokładnie MAX( COUNT(*) ) - 1razy, co może być nadal wykonalne.
DerMike,
1
cześć, to jakikolwiek sposób na uzyskanie wszystkich identyfikatorów zamiast maksymalnego identyfikatora, jak dla 2. Mogę użyć maks. i min., ale co z więcej niż 2? @DerMike
Mukherjee,
31

Możesz to zrobić w następujący sposób:

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

Jeśli chcesz zwrócić tylko rekordy, które można usunąć (pozostawiając po jednym z nich), możesz użyć:

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

Edycja: SQL Server 2000 nie ma funkcji ROW_NUMBER (). Zamiast tego możesz użyć:

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
Paweł
źródło
Pierwsze zdanie działa, ale wydaje się, że drugie nie działa.
xtine
SQL Server nie wydaje się być w stanie rozpoznać row_number ()?
xtine
Ach ... czy masz starszą wersję programu SQL Server? Wierzę, że został wprowadzony w SQL Server 2005.
Paul
3
jeszcze raz dziękuję, za każdym razem, gdy muszę to zrobić, docieram tutaj i UWIELBIAM CIEBIE
workabyte
9

Rozwiązanie oznaczone jako poprawne nie działało dla mnie, ale znalazłem odpowiedź, która zadziałała świetnie: Uzyskaj listę zduplikowanych wierszy w MySql

SELECT n1.* 
FROM myTable n1
INNER JOIN myTable n2 
ON n2.repeatedCol = n1.repeatedCol
WHERE n1.id <> n2.id
ecairol
źródło
Otrzymasz wiele duplikatów w zestawie wyników, więc będziesz musiał sobie z nimi poradzić.
Renan,
1
Jeśli identyfikator jest liczbowy, sprawdzanie n1.id > n2.idzapobiegnie dwukrotnemu wyświetlaniu się każdej pary.
głodował
9

Możesz tego spróbować, najlepiej dla Ciebie

 WITH CTE AS
    (
    SELECT *,RN=ROW_NUMBER() OVER (PARTITION BY orgName ORDER BY orgName DESC) FROM organizations 
    )
    select * from CTE where RN>1
    go
Zapisz kod
źródło
jakikolwiek sposób, aby uzyskać cały identyfikator w przecinku lub w różnych kolumnach
Arijit Mukherjee
6

Jeśli chcesz usunąć duplikaty:

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
źródło
6
select * from [Employees]

Aby znaleźć duplikat rekordu 1) Za pomocą 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) Korzystając z GroupBy

select Name,EmailId,COUNT(name) as Duplicate from  [Employees] group by Name,EmailId 
Debendra Dash
źródło
To najszybsze rozwiązanie tutaj, gdy wybiera się dane powyżej 10 m wierszy. Dzięki
Fandango68,
4
Select * from (Select orgName,id,
ROW_NUMBER() OVER(Partition By OrgName ORDER by id DESC) Rownum
From organizations )tbl Where Rownum>1

Więc rekordy z rowum> 1 będą duplikatami w twojej tabeli. „Podział według” najpierw pogrupuj według rekordów, a następnie serializuj je, nadając im numery seryjne. Więc rownum> 1 będzie zduplikowanymi rekordami, które można usunąć jako takie.

Mike Clark
źródło
Podoba mi się ten, ponieważ pozwala łatwo dodawać więcej kolumn w wewnętrznej klauzuli select. Jeśli więc chcesz zwrócić inne kolumny z tabeli „Organizacje”, nie musisz tworzyć grup według tych kolumn.
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
źródło
Działa to tylko w przypadku tabel zawierających jedną kolumnę. Co najprawdopodobniej nie jest przydatne
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
użytkownik5336758
źródło
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
źródło
1

Masz kilka sposobów na Select duplicate rows.

dla moich rozwiązań, na przykład weźmy tę tabelę na przykład

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

Pierwsze rozwiązanie:

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

Drugie rozwiązanie: użyj identitypola

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

i na końcu wszystkich rozwiązań użyj tego polecenia

DROP TABLE #Employee
Ardalan Shahgholi
źródło
0

myślę, że wiem, czego potrzebowałem, aby wymieszać odpowiedzi i myślę, że mam rozwiązanie, którego chciał:

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

Posiadanie maksymalnego identyfikatora daje identyfikator duplikatu i oryginału, o co prosił:

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)

jedyną smutną rzeczą, którą masz w tej formie

id , name , dubid , name

mam nadzieję, że to nadal pomaga

Arthur Kielbasa
źródło
0

Załóżmy, że mamy tabelę „Student” z 2 kolumnami:

  • student_id int
  • student_name varchar

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

Teraz chcemy zobaczyć zduplikowane rekordy Użyj tego zapytania:

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
źródło
0

Mam lepszą opcję uzyskania duplikatów rekordów w tabeli

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

Wynik powyższego zapytania pokazuje wszystkie zduplikowane nazwy z unikalnymi identyfikatorami studentów i liczbą zduplikowanych wystąpień

Kliknij tutaj, aby zobaczyć wynik sql

SoftIdea
źródło
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
źródło
0

Używam dwóch metod, aby znaleźć zduplikowane wiersze. Pierwsza metoda jest najbardziej znaną metodą grupowania według. Druga metoda wykorzystuje CTE - Common Table Expression .

Jak wspomniano w @RedFilter, ten sposób jest również odpowiedni. Wiele razy uważam, że metoda CTE jest również dla mnie przydatna.

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

W powyższym przykładzie zebraliśmy wynik, znajdując powtarzające się wystąpienie za pomocą ROW_NUMBER i PARTITION BY. Następnie zastosowaliśmy klauzulę where, aby wybrać tylko wiersze, które są powtarzane licząc więcej niż 1. Cały wynik jest gromadzony w tabeli CTE i łączony z tabelą Organizacje.

Źródło: CodoBee

Ishrar
źródło
-2

Próbować

SELECT orgName, id, count(*) as dupes
FROM organizations
GROUP BY orgName, id
HAVING count(*) > 1;
Ryan
źródło