Suchen nach doppelten Werten in einer SQL-Tabelle

1935

Es ist einfach, Duplikate mit einem Feld zu finden:

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

Also, wenn wir einen Tisch haben

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

Diese Abfrage gibt uns John, Sam, Tom, Tom, weil sie alle das gleiche haben email.

Was ich jedoch möchte, ist, Duplikate mit dem gleichen email und zu erhalten name .

Das heißt, ich möchte "Tom", "Tom" bekommen.

Der Grund, warum ich das brauche: Ich habe einen Fehler gemacht und durfte Duplikate nameund emailWerte einfügen . Jetzt muss ich die Duplikate entfernen / ändern, so muss ich finden sie zuerst.

Alex
quelle
28
Ich glaube nicht, dass Sie damit den Namen in Ihrem ersten Beispiel auswählen können, da es sich nicht um eine Aggregatfunktion handelt. "Was ist die Anzahl der übereinstimmenden E-Mail-Adressen und deren Namen" ist eine knifflige Logik ...
sXe
3
Es wurde festgestellt, dass dies mit dem MSSQL-Server aufgrund des nameFelds in SELECT nicht funktioniert .
E. van Putten
Was ich brauche, ist die ID der Datensätze mit doppelter E-Mail
Marcos Di Paolo

Antworten:

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

Gruppieren Sie einfach beide Spalten.

Hinweis: Der ältere ANSI-Standard sieht vor, dass alle nicht aggregierten Spalten in GROUP BY enthalten sind. Dies hat sich jedoch mit der Idee der "funktionalen Abhängigkeit" geändert :

In der relationalen Datenbanktheorie ist eine funktionale Abhängigkeit eine Einschränkung zwischen zwei Attributgruppen in einer Beziehung aus einer Datenbank. Mit anderen Worten, funktionale Abhängigkeit ist eine Einschränkung, die die Beziehung zwischen Attributen in einer Beziehung beschreibt.

Der Support ist nicht konsistent:

gbn
quelle
92
@webXL WHERE arbeitet mit Single Record HAVING arbeitet mit group
bjan
8
@gbn Ist es möglich, die ID in die Ergebnisse aufzunehmen? Dann wäre es einfacher, diese Duplikate anschließend zu löschen.
user797717
13
@ user797717: Sie müssten MIN (ID) haben und dann für ID-Werte löschen, die nicht in den letzten MIN (ID) -Werten enthalten sind
gbn
1
Was ist mit Fällen, in denen eine der Spalten Nullwerte hat?
Ankit Dhingra
1
Vielen Dank dafür, und ja, es funktioniert in Oracle, obwohl ich die Eindeutigkeit der Bedingung brauchte, also nicht>1 =1
Bill Naylor
370

Versuche dies:

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

AUSGABE:

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

(2 row(s) affected)

Wenn Sie die IDs der Dups möchten, verwenden Sie Folgendes:

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

AUSGABE:

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

(4 row(s) affected)

Um die Duplikate zu löschen, versuchen Sie:

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

AUSGABE:

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

(4 row(s) affected)
KM.
quelle
127

Versuche dies:

SELECT name, email
FROM users
GROUP BY name, email
HAVING ( COUNT(*) > 1 )
Chris Van Opstal
quelle
72

Wenn Sie die Duplikate löschen möchten, ist dies viel einfacher, als gerade / ungerade Zeilen in einer dreifachen Unterauswahl zu finden:

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

Und so zu löschen:

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
)

IMHO viel einfacher zu lesen und zu verstehen

Hinweis: Das einzige Problem besteht darin, dass Sie die Anforderung ausführen müssen, bis keine Zeilen gelöscht wurden, da Sie jedes Mal nur 1 von jedem Duplikat löschen

AncAinu
quelle
2
Schön und leicht zu lesen; Ich möchte einen Weg finden, der mehrere doppelte Zeilen auf einmal löscht.
Dickon Reed
1
Dies funktioniert nicht für mich, wie ich bekommeYou can't specify target table 'users' for update in FROM clause
Whitecat
1
@ Whitecat scheint ein einfaches MySQL-Problem zu sein: stackoverflow.com/questions/4429319/…
AncAinu
1
Schlägt für mich fehl. Ich erhalte: "DBD :: CSV :: st Ausführung fehlgeschlagen: Verwendung des nicht initialisierten Werts $ _ [1] im Hash-Element unter /Users/hornenj/perl5/perlbrew/perls/perl-5.26.0/lib/site_perl/5.26. 0 / SQL / Eval.pm Zeile 43 "
Nigel Horne
1
Ich denke, dass die where-Klausel "u.name = u2.name AND u.email = u2.email AND (u.id> u2.id OR u2.id> u.id)" sein sollte, nicht wahr?
GiveEmTheBoot
48

Versuche Folgendes:

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
quelle
3
Eine geringfügige Änderung an SELECT * half mir, eine einstündige Suche zu lösen. Ich habe das OVER (PARTITION BY noch nie zuvor verwendet). Ich bin immer wieder erstaunt darüber, wie viele Möglichkeiten es gibt, dasselbe in SQL zu tun!
Joe Ruder
33
 SELECT name, email 
    FROM users
    WHERE email in
    (SELECT email FROM users
    GROUP BY email 
    HAVING COUNT(*)>1)
PRADEEPTA VIRLLEY
quelle
28

Ein bisschen zu spät zur Party, aber ich fand eine wirklich coole Lösung, um alle doppelten IDs zu finden:

SELECT GROUP_CONCAT( id )
FROM users
GROUP BY email
HAVING ( COUNT(email) > 1 )
Indivision Dev
quelle
2
Scheint eine syntaktische Zuckerumgehung zu sein. Schöner Fund.
Chef_Code
3
Denken Sie daran, dass dies GROUP_CONCATnach einer vorgegebenen Länge aufhört, sodass Sie möglicherweise nicht alle ids erhalten.
v010dya
24

Versuchen Sie diesen Code

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

Dadurch werden alle doppelten Datensätze mit Ausnahme eines Datensatzes aus jeder Gruppe von doppelten Datensätzen ausgewählt / gelöscht. Beim Löschen bleiben also alle eindeutigen Datensätze + ein Datensatz aus jeder Gruppe der Duplikate übrig.

Duplikate auswählen:

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

Duplikate löschen:

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

Beachten Sie größere Mengen an Datensätzen, da dies zu Leistungsproblemen führen kann.

Martin Silovský
quelle
2
Fehler bei der Löschabfrage - Sie können die Zieltabelle 'Städte' für die Aktualisierung in der FROM-Klausel nicht angeben
Ali Azhar
2
Es gibt weder eine Tabelle "Städte" noch eine Aktualisierungsklausel. Was meinst du? Wo liegt ein Fehler in der Löschabfrage?
Martin Silovský
2
Wie funktioniert das mit den Daten von OP?
Thoroc
3
Was bedeutet "OP"?
Martin Silovský
19

Wenn Sie mit Oracle arbeiten, ist dieser Weg vorzuziehen:

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
quelle
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
quelle
2
Bei Stack Overflow werden nur Code-Antworten verpönt. Können Sie erklären, warum dies die Frage beantwortet?
Rich Benner
2
@RichBenner: Ich habe die Antwort nicht gefunden, wie jede einzelne Zeile im Ergebnis und welche sagt uns, welche alle doppelte Zeilen sind und welche nicht auf einen Blick und die nicht zu gruppieren sind, denn wenn wir dies kombinieren wollen Eine Abfrage mit einer anderen Abfragegruppe von ist keine gute Option.
Narendra
2
Wenn Sie der select-Anweisung eine ID hinzufügen und nach duplizierten Filtern filtern, haben Sie die Möglichkeit, die duplizierten IDs zu löschen und die IDs beizubehalten.
Antoine Reinhold Bertrand
12

Wenn Sie sehen möchten, ob Ihre Tabelle doppelte Zeilen enthält, habe ich die folgende Abfrage verwendet:

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

Das ist die einfache Sache, die ich mir ausgedacht habe. Es verwendet einen allgemeinen Tabellenausdruck (CTE) und ein Partitionsfenster (ich denke, diese Funktionen sind in SQL 2008 und höher enthalten).

In diesem Beispiel werden alle Schüler mit doppeltem Namen und dob gefunden. Die Felder, die Sie auf Duplizierung prüfen möchten, befinden sich in der OVER-Klausel. Sie können beliebige andere Felder in die Projektion aufnehmen.

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

Wie können wir die doppelten Werte zählen? Entweder wird es 2 Mal oder mehr als 2 Mal wiederholt. Zählen Sie sie einfach, nicht gruppenweise.

so einfach wie

select COUNT(distinct col_01) from Table_01
Muhammad Tahir
quelle
2
Wie würde dies für die gestellte Frage funktionieren? Dies gibt keine Zeilen an, die Informationen in mehreren Spalten (z. B. "E-Mail" und "Name") in verschiedenen Zeilen duplizieren.
Jeroen
10

Durch die Verwendung von CTE können wir auch einen doppelten Wert wie diesen finden

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
quelle
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
/
Langhaus
quelle
8

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

Ich denke, dies wird richtig funktionieren, um wiederholte Werte in einer bestimmten Spalte zu suchen.

user4877838
quelle
6
Dies fügt der Top-Antwort nichts hinzu und unterscheidet sich technisch nicht einmal wirklich von den in der Frage angegebenen Code-OPs.
Jeroen
7
SELECT * FROM users u where rowid = (select max(rowid) from users u1 where
u.email=u1.email);
Panky031
quelle
6

Dies sollte auch funktionieren, vielleicht versuchen Sie es.

  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)

Besonders gut in Ihrem Fall Wenn Sie nach Duplikaten suchen, die ein Präfix oder eine allgemeine Änderung haben, wie z. B. eine neue Domain in der E-Mail. Dann können Sie replace () in diesen Spalten verwenden

veritaS
quelle
5

Wenn Sie doppelte Daten (nach einem oder mehreren Kriterien) suchen und die tatsächlichen Zeilen auswählen möchten.

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
quelle
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
quelle
Sie können nicht COUNTohne verwenden GROUP BY, es sei denn, es bezieht sich auf die gesamte Tabelle.
RalfFriedl
Ohne Gruppe Von Sie haben COUNT verwendet, aber hier habe ich einen Tippfehler gemacht, um COUNT zu schreiben
Mohammad Neamul Islam
3

Löschen von Datensätzen, deren Namen doppelt vorhanden sind

;WITH CTE AS    
(

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

DELETE FROM CTE WHERE T > 1
Sheriff
quelle
3

So überprüfen Sie aus einem doppelten Datensatz in einer Tabelle.

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

oder

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

So löschen Sie den doppelten Datensatz in einer Tabelle.

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

oder

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

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

rahul kumar
quelle
1

Wir können hier verwenden, die an Aggregatfunktionen arbeiten, wie unten gezeigt

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

Hier werden als zwei Felder id_account und data mit Count (*) verwendet. Es werden also alle Datensätze angezeigt, die in beiden Spalten mehr als einmal die gleichen Werte haben.

Wir haben aus irgendeinem Grund fälschlicherweise versäumt, Einschränkungen in die SQL Server-Tabelle einzufügen, und die Datensätze wurden in allen Spalten mit Front-End-Anwendung doppelt eingefügt. Dann können wir die folgende Abfrage verwenden, um doppelte Abfragen aus der Tabelle zu löschen.

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

Hier haben wir alle unterschiedlichen Datensätze der ursprünglichen Tabelle genommen und die Datensätze der ursprünglichen Tabelle gelöscht. Wieder haben wir alle unterschiedlichen Werte von der neuen Tabelle in die ursprüngliche Tabelle eingefügt und dann die neue Tabelle gelöscht.

Suraj Kumar
quelle
1

Vielleicht möchten Sie dies versuchen

SELECT NAME, EMAIL, COUNT(*)
FROM USERS
GROUP BY 1,2
HAVING COUNT(*) > 1
adesh
quelle
1

Das Wichtigste dabei ist, die schnellste Funktion zu haben. Auch Indizes von Duplikaten sollten identifiziert werden. Self Join ist eine gute Option, aber um eine schnellere Funktion zu haben, ist es besser, zuerst Zeilen mit Duplikaten zu suchen und dann mit der Originaltabelle zu verbinden, um die ID von doppelten Zeilen zu ermitteln. Ordnen Sie schließlich nach einer beliebigen Spalte außer id, dass doppelte Zeilen nebeneinander vorhanden sind.

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

Sie können das Schlüsselwort SELECT DISTINCT verwenden, um Duplikate zu entfernen. Sie können auch nach Namen filtern und alle Personen mit diesem Namen in eine Tabelle aufnehmen.

Parkofadown
quelle
0

Der genaue Code hängt davon ab, ob Sie auch doppelte Zeilen oder nur unterschiedliche IDs mit derselben E-Mail-Adresse und demselben Namen suchen möchten. Wenn id ein Primärschlüssel ist oder auf andere Weise eine eindeutige Einschränkung aufweist, existiert diese Unterscheidung nicht, aber die Frage gibt dies nicht an. Im ersteren Fall können Sie Code verwenden, der in mehreren anderen Antworten angegeben ist:

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

Im letzteren Fall würden Sie verwenden:

SELECT name, email, COUNT(DISTINCT id)
FROM users
GROUP BY name, email
HAVING COUNT(DISTINCT id) > 1
ORDER BY COUNT(DISTINCT id) DESC
RET
quelle