Wie lösche ich doppelte Zeilen in SQL Server?


415

Wie kann ich doppelte Zeilen löschen, wenn keine unique row idvorhanden sind?

Mein Tisch ist

col1  col2 col3 col4 col5 col6 col7
john  1    1    1    1    1    1 
john  1    1    1    1    1    1
sally 2    2    2    2    2    2
sally 2    2    2    2    2    2

Ich möchte nach dem Entfernen des Duplikats Folgendes haben:

john  1    1    1    1    1    1
sally 2    2    2    2    2    2

Ich habe einige Abfragen ausprobiert, aber ich denke, sie hängen von einer Zeilen-ID ab, da ich nicht das gewünschte Ergebnis erhalte. Zum Beispiel:

DELETE
FROM table
WHERE col1 IN (
    SELECT id
    FROM table
    GROUP BY id
    HAVING (COUNT(col1) > 1)
)

5
Dies ist kein Betrug des ersten Links. In dieser Frage gibt es keine Zeilen-ID und in der verknüpften Frage gibt es eine Zeilen-ID. Sehr verschieden.
Alien Technology

Ändern Sie 'SELECT id FROM table GROUP BY id HAVING', um eine aggregierte Funktion zu haben, z. B. MAX / MIN, und es sollte funktionieren.
durcheinander

Antworten:


785

Ich mag CTEs und ROW_NUMBERda wir beide zusammen sehen können, welche Zeilen gelöscht (oder aktualisiert) werden, ändern Sie einfach Folgendes DELETE FROM CTE...in SELECT * FROM CTE:

WITH CTE AS(
   SELECT [col1], [col2], [col3], [col4], [col5], [col6], [col7],
       RN = ROW_NUMBER()OVER(PARTITION BY col1 ORDER BY col1)
   FROM dbo.Table1
)
DELETE FROM CTE WHERE RN > 1

DEMO (Ergebnis ist anders; ich gehe davon aus, dass es an einem Tippfehler von Ihrer Seite liegt)

COL1    COL2    COL3    COL4    COL5    COL6    COL7
john    1        1       1       1       1       1
sally   2        2       2       2       2       2

In diesem Beispiel werden Duplikate col1aufgrund der PARTITION BY col1. Wenn Sie mehrere Spalten einfügen möchten, fügen Sie diese einfach hinzu PARTITION BY:

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)

2
Vielen Dank für eine tolle Antwort. Im Gegensatz dazu hat MSFT hier eine sehr komplizierte Antwort: stackoverflow.com/questions/18390574/…
Barka

2
@ omachu23: In diesem Fall spielt es keine Rolle, obwohl ich denke, dass es im CTE effizienter ist als außerhalb ( AND COl1='John'). Normalerweise sollten Sie den Filter im CTE anwenden.
Tim Schmelter

1
@ omachu23: Sie können jedes SQL im CTE verwenden (außer der Bestellung). Wenn Sie also nach Johns filtern möchten : ...FROM dbo.Table1 WHERE Col1='John'. Hier ist die Geige: sqlfiddle.com/#!6/fae73/744/0
Tim Schmelter

1
Die einfachste Lösung kann genau set rowcount 1 delete from t1 where col1=1 and col2=1wie hier
Zorgarath

15
Diese Antwort löscht nur die Zeilen mit Duplikaten in Spalte 1. Fügen Sie die Spalten in "select" zu "partition by" hinzu, z. B. mit select in der Antwort: RN = ROW_NUMBER () OVER (PARTITION BY col1, col2, col3, col4, col5, col6, col7 ORDER BY col1)
Rlee

158

Ich würde CTE bevorzugen, um doppelte Zeilen aus der SQL Server-Tabelle zu löschen

Es wird dringend empfohlen, diesem Artikel zu folgen: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

indem Sie das Original behalten

WITH CTE AS
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY col1,col2,col3 ORDER BY col1,col2,col3) AS RN
FROM MyTable
)

DELETE FROM CTE WHERE RN<>1

ohne das Original zu behalten

WITH CTE AS
(SELECT *,R=RANK() OVER (ORDER BY col1,col2,col3)
FROM MyTable)
 
DELETE CTE
WHERE R IN (SELECT R FROM CTE GROUP BY R HAVING COUNT(*)>1)

2
Die Fensterfunktion ist eine großartige Lösung.
Robert Casey

2
Ich bin wenig verwirrt. Sie haben es aus CTE gelöscht, nicht aus der Originaltabelle. Wie funktioniert es?
Großaugen

8
@Bigeyes Durch das Löschen von Datensätzen aus CTE werden entsprechende Datensätze aus der tatsächlichen physischen Tabelle entfernt (da CTE Verweise auf tatsächliche Datensätze enthält).
Shamseer K

Ich hatte keine Ahnung, dass dies der Fall war, bis dieser Beitrag ... Danke
Zakk Diaz

1
Warum sollten Sie sowohl das Original als auch das Duplikat löschen wollen? Ich verstehe nicht, warum Sie nicht einfach das Duplikat entfernen und das andere behalten möchten.
Rich

52

Ohne zu verwenden CTEund ROW_NUMBER()Sie können die Datensätze einfach löschen, indem Sie group by mit MAXFunktion hier und Beispiel verwenden

DELETE
FROM MyDuplicateTable
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM MyDuplicateTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)

4
Diese Abfrage löscht nicht doppelte Datensätze.
Derek Smalls

8
Das funktioniert gut, danke. @DerekSmalls dies entfernt nicht meine nicht doppelten Datensätze.
Monteirobrena

1
Oder Sie können die Originalaufzeichnungen mitMIN(ID)
Savage

18
DELETE from search
where id not in (
   select min(id) from search
   group by url
   having count(*)=1

   union

   SELECT min(id) FROM search
   group by url
   having count(*) > 1
)

Könnten Sie nicht erneut schreiben an: where id in (wählen Sie max (id) ... mit count (*)> 1)?
Brent

1
Ich glaube nicht, dass es notwendig ist, mit oder Vereinigung zu haben, dies wird ausreichen: aus der Suche löschen, wo ID nicht in (wählen Sie min (ID) aus der Suchgruppe nach URL)
Christopher Yang

9

Bitte beachten Sie auch die unten stehende Art der Löschung.

Declare @table table
(col1 varchar(10),col2 int,col3 int, col4 int, col5 int, col6 int, col7 int)
Insert into @table values 
('john',1,1,1,1,1,1),
('john',1,1,1,1,1,1),
('sally',2,2,2,2,2,2),
('sally',2,2,2,2,2,2)

Erstellt eine Beispieltabelle mit dem Namen @tableund lädt sie mit den angegebenen Daten.

Geben Sie hier die Bildbeschreibung ein

Delete  aliasName from (
Select  *,
        ROW_NUMBER() over (Partition by col1,col2,col3,col4,col5,col6,col7 order by col1) as rowNumber
From    @table) aliasName 
Where   rowNumber > 1

Select * from @table

Geben Sie hier die Bildbeschreibung ein

Hinweis: Wenn Sie alle Spalten im Partition byTeil angeben, hat order bydies keine große Bedeutung.

Ich weiß, die Frage wurde vor drei Jahren gestellt, und meine Antwort ist eine andere Version dessen, was Tim gepostet hat. Aber das Posten ist nur für den Fall hilfreich, dass es für jeden hilfreich ist.


9

Wenn Sie keine Referenzen wie Fremdschlüssel haben, können Sie dies tun. Ich mache es oft, wenn ich Proofs of Concept teste und die Testdaten dupliziert werden.

SELECT DISTINCT [col1],[col2],[col3],[col4],[col5],[col6],[col7]

INTO [newTable]

Gehen Sie in den Objekt-Explorer und löschen Sie die alte Tabelle.

Benennen Sie die neue Tabelle mit dem Namen der alten Tabelle um.


Dies ist der einfachste Weg, den ich in meinen Intro-Materialien gelernt habe und den ich benutze.
Eric

7

Microsoft hat eine sehr übersichtliche Anleitung zum Entfernen von Duplikaten. Überprüfen Sie http://support.microsoft.com/kb/139444

Kurz gesagt, hier ist der einfachste Weg, um Duplikate zu löschen, wenn Sie nur wenige Zeilen zum Löschen haben:

SET rowcount 1;
DELETE FROM t1 WHERE myprimarykey=1;

myprimarykey ist die Kennung für die Zeile.

Ich habe die Zeilenanzahl auf 1 gesetzt, weil ich nur zwei Zeilen hatte, die dupliziert wurden. Wenn ich 3 Zeilen dupliziert hätte, hätte ich die Zeilenanzahl auf 2 gesetzt, damit die ersten beiden Zeilen gelöscht werden und nur eine in Tabelle t1 verbleibt.

Hoffe es hilft jedem


1
Woher weiß ich, wie viele Zeilen ich dupliziert habe, wenn ich 10.000 Zeilen habe?
Fearghal

@Fearghal try "wähle Primärschlüssel aus, zähle (*) aus der myTable-Gruppe nach Primärschlüssel;"
Oabarca

1
Was aber, wenn es unterschiedlich viele doppelte Zeilen gibt? dh Zeile a hat 2 Datensätze und Zeile b hat 5 Datensätze und Zeile c hat keine doppelten Datensätze
Thermit

1
@ user2070775 Was ist, wenn nur eine Teilmenge aller Zeilen Duplikate enthält und einige dieser Duplikate zweimal und einige drei- oder viermal dupliziert werden?
Thermit

@ user2070775 Ich habe den Teil verpasst, in dem Sie "nur ein paar Zeilen zum Löschen" gesagt haben. Es gibt auch eine Warnung auf der Seite über das Festlegen der Zeilenanzahl, dass es in zukünftigen Versionen von SQL keine Auswirkungen auf Aktualisierungs- oder Löschanweisungen hat
Thermite

6

Versuchen zu benutzen:

SELECT linkorder
    ,Row_Number() OVER (
        PARTITION BY linkorder ORDER BY linkorder DESC
        ) AS RowNum
FROM u_links

Geben Sie hier die Bildbeschreibung ein


4

Nachdem Sie die oben vorgeschlagene Lösung ausprobiert haben, funktioniert dies für kleine mittlere Tabellen. Ich kann diese Lösung für sehr große Tabellen vorschlagen. da es in Iterationen läuft.

  1. Löschen Sie alle Abhängigkeitsansichten des LargeSourceTable
  2. Sie können die Abhängigkeiten mithilfe von SQL Management Studio ermitteln. Klicken Sie mit der rechten Maustaste auf die Tabelle und klicken Sie auf "Abhängigkeiten anzeigen".
  3. Benennen Sie die Tabelle um:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. Erstellen Sie das LargeSourceTableerneut, aber fügen Sie jetzt einen Primärschlüssel mit allen Spalten hinzu, die das Hinzufügen von Duplikaten definierenWITH (IGNORE_DUP_KEY = ON)
  6. Zum Beispiel:

    CREATE TABLE [dbo].[LargeSourceTable] ( ID int IDENTITY(1,1), [CreateDate] DATETIME CONSTRAINT [DF_LargeSourceTable_CreateDate] DEFAULT (getdate()) NOT NULL, [Column1] CHAR (36) NOT NULL, [Column2] NVARCHAR (100) NOT NULL, [Column3] CHAR (36) NOT NULL, PRIMARY KEY (Column1, Column2) WITH (IGNORE_DUP_KEY = ON) ); GO

  7. Erstellen Sie erneut die Ansichten, die Sie an erster Stelle für die neu erstellte Tabelle gelöscht haben

  8. Führen Sie nun das folgende SQL-Skript aus. Die Ergebnisse werden in 1.000.000 Zeilen pro Seite angezeigt. Sie können die Zeilennummer pro Seite ändern, um die Ergebnisse häufiger anzuzeigen.

  9. Beachten Sie, dass ich das IDENTITY_INSERTEin- und Ausschalten aktiviert habe, da eine der Spalten eine automatisch inkrementelle ID enthält, die ich auch kopiere

SET IDENTITY_INSERT LargeSourceTable ON DECLARE @PageNumber AS INT, @RowspPage AS INT DECLARE @TotalRows AS INT declare @dt varchar(19) SET @PageNumber = 0 SET @RowspPage = 1000000 select @TotalRows = count (*) from LargeSourceTable_TEMP

While ((@PageNumber - 1) * @RowspPage < @TotalRows )
Begin
    begin transaction tran_inner
        ; with cte as
        (
            SELECT * FROM LargeSourceTable_TEMP ORDER BY ID
            OFFSET ((@PageNumber) * @RowspPage) ROWS
            FETCH NEXT @RowspPage ROWS ONLY
        )

        INSERT INTO LargeSourceTable 
        (
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        )       
        select 
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        from cte

    commit transaction tran_inner

    PRINT 'Page: ' + convert(varchar(10), @PageNumber)
    PRINT 'Transfered: ' + convert(varchar(20), @PageNumber * @RowspPage)
    PRINT 'Of: ' + convert(varchar(20), @TotalRows)

    SELECT @dt = convert(varchar(19), getdate(), 121)
    RAISERROR('Inserted on: %s', 0, 1, @dt) WITH NOWAIT
    SET @PageNumber = @PageNumber + 1
End

SET IDENTITY_INSERT LargeSourceTable OFF


4

Es gibt zwei Lösungen in mysql:

A) Löschen Sie doppelte Zeilen mit der DELETE JOINAnweisung

DELETE t1 FROM contacts t1
INNER JOIN contacts t2 
WHERE 
    t1.id < t2.id AND 
    t1.email = t2.email;

Diese Abfrage verweist zweimal auf die Kontakttabelle t1und verwendet daher den Tabellenalias und t2.

Die Ausgabe ist:

1 Abfrage OK, 4 Zeilen betroffen (0,10 Sek.)

Wenn Sie doppelte Zeilen löschen und die beibehalten möchten lowest id, können Sie die folgende Anweisung verwenden:

DELETE c1 FROM contacts c1
INNER JOIN contacts c2 
WHERE
    c1.id > c2.id AND 
    c1.email = c2.email;

   

B) Löschen Sie doppelte Zeilen mithilfe einer Zwischentabelle

Im Folgenden werden die Schritte zum Entfernen doppelter Zeilen mithilfe einer Zwischentabelle aufgeführt:

    1. Erstellen Sie eine neue Tabelle mit der gleichen Struktur wie die ursprüngliche Tabelle, in der Sie doppelte Zeilen löschen möchten.

    2. Fügen Sie unterschiedliche Zeilen aus der Originaltabelle in die Soforttabelle ein.

    3. Fügen Sie unterschiedliche Zeilen aus der ursprünglichen Tabelle in die unmittelbare Tabelle ein.

 

Schritt 1. Erstellen Sie eine neue Tabelle, deren Struktur mit der ursprünglichen Tabelle übereinstimmt:

CREATE TABLE source_copy LIKE source;

Schritt 2. Fügen Sie unterschiedliche Zeilen aus der ursprünglichen Tabelle in die neue Tabelle ein:

INSERT INTO source_copy
SELECT * FROM source
GROUP BY col; -- column that has duplicate values

Schritt 3. Löschen Sie die ursprüngliche Tabelle und benennen Sie die unmittelbare Tabelle in die ursprüngliche um

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

Quelle: http://www.mysqltutorial.org/mysql-delete-duplicate-rows/


2
-- this query will keep only one instance of a duplicate record.
;WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY col1, col2, col3-- based on what? --can be multiple columns
                                       ORDER BY ( SELECT 0)) RN
         FROM   Mytable)



delete  FROM cte
WHERE  RN > 1

2

Sie müssen nach den doppelten Datensätzen entsprechend den Feldern gruppieren, dann einen der Datensätze halten und den Rest löschen. Zum Beispiel:

DELETE prg.Person WHERE Id IN (
SELECT dublicateRow.Id FROM
(
select MIN(Id) MinId, NationalCode
 from  prg.Person group by NationalCode  having count(NationalCode ) > 1
 ) GroupSelect
 JOIN  prg.Person dublicateRow ON dublicateRow.NationalCode = GroupSelect.NationalCode 
 WHERE dublicateRow.Id <> GroupSelect.MinId)

2

Das Löschen von Duplikaten aus einer riesigen Tabelle (mehrere Millionen Datensätze) kann lange dauern. Ich schlage vor, dass Sie eine Masseneinfügung in eine temporäre Tabelle der ausgewählten Zeilen durchführen, anstatt sie zu löschen.

--REWRITING YOUR CODE(TAKE NOTE OF THE 3RD LINE) WITH CTE AS(SELECT NAME,ROW_NUMBER() 
OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB) SELECT * INTO #unique_records FROM 
CTE WHERE ID =1;

2

In SQL Server kann dies auf viele Arten erfolgen. Die einfachste Möglichkeit hierfür ist: Einfügen der einzelnen Zeilen aus der Tabelle der doppelten Zeilen in eine neue temporäre Tabelle. Löschen Sie dann alle Daten aus der Tabelle mit doppelten Zeilen und fügen Sie alle Daten aus der temporären Tabelle ein, die keine Duplikate enthält, wie unten gezeigt.

select distinct * into #tmp From table
   delete from table
   insert into table
   select * from #tmp drop table #tmp

   select * from table

Löschen Sie doppelte Zeilen mit Common Table Expression (CTE).

With CTE_Duplicates as 
(select id,name , row_number() 
over(partition by id,name order by id,name ) rownumber  from table  ) 
delete from CTE_Duplicates where rownumber!=1

1
with myCTE
as

(
select productName,ROW_NUMBER() over(PARTITION BY productName order by slno) as Duplicate from productDetails
)
Delete from myCTE where Duplicate>1

1

Unter Bezugnahme auf https://support.microsoft.com/en-us/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server

Die Idee, Duplikate zu entfernen, beinhaltet

  • a) Schützen Sie die Zeilen, die nicht doppelt vorhanden sind
  • b) Behalten Sie eine der vielen Zeilen bei, die zusammen als Duplikat qualifiziert wurden.

Schritt für Schritt

  • 1) Identifizieren Sie zuerst die Zeilen, die der Definition von Duplikat entsprechen, und fügen Sie sie in die temporäre Tabelle ein, z. B. #tableAll.
  • 2) Wählen Sie nicht doppelte (einzelne Zeilen) oder unterschiedliche Zeilen in der temporären Tabelle aus, z. B. #tableUnique.
  • 3) Aus der Quelltabelle löschen und #tableAll verbinden, um die Duplikate zu löschen.
  • 4) Fügen Sie alle Zeilen von #tableUnique in die Quelltabelle ein.
  • 5) Löschen Sie #tableAll und #tableUnique

1

Wenn Sie der Tabelle vorübergehend eine Spalte hinzufügen können, war dies eine Lösung, die für mich funktioniert hat:

ALTER TABLE dbo.DUPPEDTABLE ADD RowID INT NOT NULL IDENTITY(1,1)

Führen Sie dann ein LÖSCHEN mit einer Kombination aus MIN und GROUP BY durch

DELETE b
FROM dbo.DUPPEDTABLE b
WHERE b.RowID NOT IN (
                     SELECT MIN(RowID) AS RowID
                     FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
                     GROUP BY a.ITEM_NUMBER,
                              a.CHARACTERISTIC,
                              a.INTVALUE,
                              a.FLOATVALUE,
                              a.STRINGVALUE
                 );

Stellen Sie sicher, dass DELETE ordnungsgemäß ausgeführt wurde:

SELECT a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE, COUNT(*)--MIN(RowID) AS RowID
FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
GROUP BY a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE
ORDER BY COUNT(*) DESC 

Das Ergebnis sollte keine Zeilen mit einer Anzahl größer als 1 enthalten. Entfernen Sie schließlich die Zeilen-ID-Spalte:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;

0

Eine andere Möglichkeit, nicht veröffentlichte Zeilen zu entfernen, ohne Informationen in einem Schritt zu verlieren, ist wie folgt:

delete from dublicated_table t1 (nolock)
join (
    select t2.dublicated_field
    , min(len(t2.field_kept)) as min_field_kept
    from dublicated_table t2 (nolock)
    group by t2.dublicated_field having COUNT(*)>1
) t3 
on t1.dublicated_field=t3.dublicated_field 
    and len(t1.field_kept)=t3.min_field_kept

0

Oh wow, ich fühle mich so dumm, wenn ich all diese Antworten fertig habe. Sie sind wie die Antwort von Experten mit allen CTE- und temporären Tabellen usw.

Und alles, was ich getan habe, um es zum Laufen zu bringen, war einfach die ID-Spalte mit MAX zu aggregieren.

DELETE FROM table WHERE col1 IN (
    SELECT MAX(id) FROM table GROUP BY id HAVING ( COUNT(col1) > 1 )
)

HINWEIS: Möglicherweise müssen Sie es mehrmals ausführen, um doppelte Zeilen zu entfernen, da dadurch jeweils nur ein Satz doppelter Zeilen gelöscht wird.


Dies funktioniert nicht, da alle Duplikate entfernt werden, ohne die Originale zu verlassen. OP bittet darum, die Originalaufzeichnungen beizubehalten.
0xdd

2
Nicht wahr, max gibt Ihnen die maximale ID, die die Bedingung erfüllt. Wenn dies nicht der Fall ist, beweisen Sie Ihren Fall für eine Abwärtsabstimmung.
durcheinander

0
DECLARE @TB TABLE(NAME VARCHAR(100));
INSERT INTO @TB VALUES ('Red'),('Red'),('Green'),('Blue'),('White'),('White')
--**Delete by Rank**
;WITH CTE AS(SELECT NAME,DENSE_RANK() OVER (PARTITION BY NAME ORDER BY NEWID()) ID FROM @TB)
DELETE FROM CTE WHERE ID>1
SELECT NAME FROM @TB;
--**Delete by Row Number** 
;WITH CTE AS(SELECT NAME,ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB)
DELETE FROM CTE WHERE ID>1;
SELECT NAME FROM @TB;

Das Löschen von Duplikaten aus einer riesigen Tabelle (mehrere Millionen Datensätze) kann lange dauern. Ich schlage vor, dass Sie eine Masseneinfügung in eine temporäre Tabelle der ausgewählten Zeilen vornehmen, anstatt sie zu löschen. '- Umschreiben Ihres Codes (beachten Sie die 3. Zeile) mit CTE AS (SELECT NAME, ROW_NUMBER () OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @ TB) SELECT * INTO #unique_records FROM CTE WHERE ID = 1; '
Emmanuel Bull

0
DELETE FROM TBL1  WHERE ID  IN
(SELECT ID FROM TBL1  a WHERE ID!=
(select MAX(ID) from TBL1  where DUPVAL=a.DUPVAL 
group by DUPVAL
having count(DUPVAL)>1))
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.