Znajdowanie zduplikowanych wartości w tabeli SQL

1931

Łatwo jest znaleźć duplikaty za pomocą jednego pola:

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

Więc jeśli mamy stolik

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

To zapytanie da nam Johna, Sama, Toma, Toma, ponieważ wszyscy mają to samo email.

Jednak chcę uzyskać duplikaty z tym samym email i name .

To znaczy, chcę dostać „Tom”, „Tom”.

Powód, dla którego go potrzebuję: popełniłem błąd i pozwoliłem wstawić duplikat namei emailwartości. Teraz muszę usunąć / zmienić duplikaty, więc najpierw muszę je znaleźć .

Alex
źródło
28
Nie sądzę, żeby pozwolił ci wybrać nazwę w pierwszej próbce, ponieważ nie jest to funkcja agregująca. „Jaka jest liczba pasujących adresów e-mail i ich nazw” to pewna trudna logika ...
sXe
3
Stwierdzono, że nie działa to z serwerem MSSQL z powodu namepola w SELECT.
E. van Putten,
potrzebuję identyfikatora rekordów ze zduplikowanym
Marcos Di Paolo

Odpowiedzi:

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

Po prostu zgrupuj obie kolumny.

Uwaga: starszy standard ANSI ma mieć wszystkie niezagregowane kolumny w GROUP BY, ale zmieniło się to wraz z ideą „zależności funkcjonalnej” :

W teorii relacyjnych baz danych zależność funkcjonalna jest ograniczeniem między dwoma zestawami atrybutów w relacji z bazy danych. Innymi słowy, zależność funkcjonalna jest ograniczeniem opisującym związek między atrybutami w relacji.

Wsparcie nie jest spójne:

gbn
źródło
92
@webXL GDZIE działa z pojedynczym rekordem HAVING współpracuje z grupą
bjan
8
@gbn Czy można uwzględnić identyfikator w wynikach? Wtedy łatwiej byłoby później usunąć te duplikaty.
user797717
13
@ user797717: musisz mieć MIN (ID), a następnie usunąć wartości identyfikatora nie z ostatnich, jeśli wartości MIN (ID)
gbn
1
Co z przypadkami, w których którakolwiek z kolumn ma wartości null?
Ankit Dhingra
1
Dzięki bardzo za to i tak, to działa w Oracle, chociaż potrzebowałem wyjątkowości tego warunku, więc zamiast>1 =1
Bill Naylor
370

Spróbuj tego:

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

WYNIK:

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

(2 row(s) affected)

jeśli chcesz mieć identyfikatory duplikatów, użyj tego:

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

WYNIK:

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

(4 row(s) affected)

aby usunąć duplikaty, spróbuj:

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

WYNIK:

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

(4 row(s) affected)
KM.
źródło
127

Spróbuj tego:

SELECT name, email
FROM users
GROUP BY name, email
HAVING ( COUNT(*) > 1 )
Chris Van Opstal
źródło
72

Jeśli chcesz usunąć duplikaty, oto o wiele prostszy sposób, aby to zrobić, niż znajdowanie parzystych / nieparzystych wierszy w potrójnym podselekcji:

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

I tak, aby usunąć:

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
)

Znacznie łatwiejsze do odczytania i zrozumienia IMHO

Uwaga: Jedynym problemem jest to, że musisz wykonać żądanie, dopóki nie zostaną usunięte wiersze, ponieważ za każdym razem usuwasz tylko 1 każdego duplikatu

AncAinu
źródło
2
Ładne i łatwe do odczytania; Chciałbym jednak znaleźć sposób, aby usunąć wiele zduplikowanych wierszy za jednym razem.
Dickon Reed,
1
To mi nie działa, kiedy dostajęYou can't specify target table 'users' for update in FROM clause
Whitecat
1
@ Whitecat wydaje się prostym problemem MySQL: stackoverflow.com/questions/4429319/…
AncAinu
1
Zawodzi dla mnie. Dostaję: „Wykonanie DBD :: CSV :: st nie powiodło się: użycie niezainicjowanej wartości $ _ [1] w elemencie haszującym w /Users/hornenj/perl5/perlbrew/perls/perl-5.26.0/lib/site_perl/5.26. 0 / SQL / Eval.pm linia 43 "
Nigel Horne
1
Myślę, że gdzie klauzula powinna brzmieć „u.name = u2.name AND u.email = u2.email AND (u.id> u2.id OR u2.id> u.id)”, prawda?
GiveEmTheBoot
48

Spróbuj wykonać następujące czynności:

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
źródło
3
Niewielka zmiana na SELECT * pomogła mi rozwiązać godzinę wyszukiwania. Nigdy wcześniej nie korzystałem z opcji OVER (PARTITION BY. Nigdy nie przestaję dziwić się, jak wiele sposobów na zrobienie tego samego w SQL!
Joe Ruder,
33
 SELECT name, email 
    FROM users
    WHERE email in
    (SELECT email FROM users
    GROUP BY email 
    HAVING COUNT(*)>1)
PRADEEPTA VIRLLEY
źródło
28

Trochę późno na imprezę, ale znalazłem naprawdę fajne obejście, aby znaleźć wszystkie duplikaty identyfikatorów:

SELECT GROUP_CONCAT( id )
FROM users
GROUP BY email
HAVING ( COUNT(email) > 1 )
Indivision Dev
źródło
2
Wydaje się być obejściem cukru syntaktycznego. Niezłe znalezisko.
Chef_Code
3
Pamiętaj, że GROUP_CONCATzatrzyma się po określonej z góry długości, więc możesz nie dostać wszystkich id.
v010dya,
24

wypróbuj ten kod

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

To wybiera / usuwa wszystkie zduplikowane rekordy, z wyjątkiem jednego rekordu z każdej grupy duplikatów. Usunięcie pozostawia wszystkie unikalne rekordy + jeden rekord z każdej grupy duplikatów.

Wybierz duplikaty:

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

Usuń duplikaty:

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

Należy pamiętać o większej liczbie rekordów, może to powodować problemy z wydajnością.

Martin Silovský
źródło
2
Błąd w usunięciu zapytania - Nie można określić tabeli docelowej „miasta” do aktualizacji w klauzuli FROM
Ali Azhar
2
Nie ma ani tabeli „miast”, ani klauzuli dotyczącej aktualizacji. Co masz na myśli? Gdzie jest błąd w kwerendzie usuwania?
Martin Silovský 16.04.18
2
Jak to działa z danymi OP?
thoroc
3
Co oznacza „OP”?
Martin Silovský
19

W przypadku pracy z Oracle preferowany jest ten sposób:

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
źródło
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
źródło
2
Na przepełnieniu stosu pojawiają się tylko odpowiedzi na kod. Czy możesz wyjaśnić, dlaczego to odpowiada na pytanie?
Rich Benner,
2
@RichBenner: Nie znalazłem odpowiedzi, takiej jak, każdy i każdy wiersz w wyniku i która mówi nam, które wszystkie są zduplikowanymi wierszami, a które nie są w jednym spojrzeniu, a które nie grupować, ponieważ jeśli chcemy to połączyć zapytanie z dowolną inną grupą zapytań według nie jest dobrą opcją.
Narendra,
2
Dodanie identyfikatora do instrukcji select i filtrowanie zduplikowanych danych daje możliwość usunięcia zduplikowanych identyfikatorów i zachowania każdego z nich.
Antoine Reinhold Bertrand
12

Jeśli chcesz sprawdzić, czy w tabeli są zduplikowane wiersze, użyłem poniższego zapytania:

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

To jest prosta rzecz, którą wymyśliłem. Używa wspólnego wyrażenia tabelowego (CTE) i okna partycji (myślę, że te funkcje są w SQL 2008 i późniejszych wersjach).

W tym przykładzie wszyscy uczniowie mają zduplikowane imię i nazwisko. Pola, które chcesz sprawdzić pod kątem duplikacji, znajdują się w klauzuli OVER. Możesz dołączyć dowolne inne pola do rzutu.

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
źródło
11
select id,name,COUNT(*) from user group by Id,Name having COUNT(*)>1
Debendra Dash
źródło
10

Jak możemy policzyć zduplikowane wartości? powtarza się 2 razy lub więcej niż 2. po prostu je policz, a nie grupowo.

tak prosty jak

select COUNT(distinct col_01) from Table_01
Muhammad Tahir
źródło
2
Jak to zadziała w przypadku zadanego pytania? Ten sposób nie dać wiersze duplikat informacji w wielu kolumnach (np „e-mail” i „nazwa”) w różnych rzędach.
Jeroen
10

Korzystając z CTE, możemy również znaleźć taką duplikat wartości

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

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

Myślę, że to zadziała poprawnie, aby wyszukać powtarzające się wartości w określonej kolumnie.

użytkownik4877838
źródło
6
Nie dodaje to niczego do najwyższej odpowiedzi i technicznie nie różni się tak naprawdę od kodu OP opublikowanego w pytaniu.
Jeroen
7
SELECT * FROM users u where rowid = (select max(rowid) from users u1 where
u.email=u1.email);
Panky031
źródło
6

To powinno również działać, może spróbować.

  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)

Szczególnie dobrze w twoim przypadku, gdy szukasz duplikatów, które mają jakiś prefiks lub ogólną zmianę, np. Nowa domena w poczcie. wtedy możesz użyć replace () w tych kolumnach

VeritaS
źródło
5

Jeśli chcesz znaleźć zduplikowane dane (według jednego lub kilku kryteriów) i wybierz rzeczywiste wiersze.

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
źródło
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
źródło
Nie możesz używać COUNTbez GROUP BY, chyba że odnosi się to do całego stołu.
RalfFriedl
Bez grupy By użyłeś COUNT, ale tutaj popełniam błąd w pisaniu, żeby napisać COUNT
Mohammad Neamul Islam
3

Aby usunąć rekordy, których nazwy są zduplikowane

;WITH CTE AS    
(

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

DELETE FROM CTE WHERE T > 1
Szeryf
źródło
3

Aby sprawdzić ze zduplikowanego rekordu w tabeli.

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

lub

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

Aby usunąć duplikat rekordu z tabeli.

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

lub

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

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

Rahul Kumar
źródło
1

Możemy skorzystać z posiadania tutaj, które działają na funkcje agregujące, jak pokazano poniżej

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

Tutaj jako dwa pola id_account i dane są używane z Count (*). Daje to wszystkie rekordy, które mają więcej niż jeden raz te same wartości w obu kolumnach.

Z jakiegoś powodu omyłkowo pominęliśmy dodanie jakichkolwiek ograniczeń w tabeli serwera SQL, a rekordy zostały wstawione zduplikowane we wszystkich kolumnach z aplikacją front-end. Następnie możemy użyć poniższego zapytania, aby usunąć zduplikowane zapytanie z tabeli.

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

Tutaj pobraliśmy wszystkie odrębne rekordy oryginalnej tabeli i usunęliśmy rekordy oryginalnej tabeli. Ponownie wstawiliśmy wszystkie odrębne wartości z nowej tabeli do oryginalnej tabeli, a następnie usunęliśmy nową tabelę.

Suraj Kumar
źródło
1

Możesz spróbować tego

SELECT NAME, EMAIL, COUNT(*)
FROM USERS
GROUP BY 1,2
HAVING COUNT(*) > 1
adesh
źródło
1

Najważniejsze jest tutaj, aby mieć najszybszą funkcję. Należy również zidentyfikować wskaźniki duplikatów. Samozłączenie jest dobrą opcją, ale aby mieć szybszą funkcję, lepiej najpierw znaleźć wiersze, które mają duplikaty, a następnie połączyć z oryginalną tabelą, aby znaleźć identyfikator zduplikowanych wierszy. Na koniec uporządkuj według dowolnej kolumny oprócz id, aby mieć zduplikowane wiersze blisko siebie.

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

Możesz użyć słowa kluczowego SELECT DISTINCT, aby pozbyć się duplikatów. Możesz także filtrować według nazwy i umieszczać wszystkich z tą nazwą na stole.

Parkofadown
źródło
0

Dokładny kod różni się w zależności od tego, czy chcesz znaleźć zduplikowane wiersze, czy tylko różne identyfikatory o tym samym adresie e-mail i nazwie. Jeśli identyfikator jest kluczem podstawowym lub w inny sposób ma unikalne ograniczenie, to rozróżnienie nie istnieje, ale pytanie tego nie określa. W pierwszym przypadku możesz użyć kodu podanego w kilku innych odpowiedziach:

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

W tym drugim przypadku użyłbyś:

SELECT name, email, COUNT(DISTINCT id)
FROM users
GROUP BY name, email
HAVING COUNT(DISTINCT id) > 1
ORDER BY COUNT(DISTINCT id) DESC
GNIĆ
źródło