Frage

Was ist der beste Weg, doppelte Zeilen aus einer ziemlich großen Datei zu entfernen? SQL Server Tabelle (d. h.300.000+ Zeilen)?

Aufgrund der Existenz von sind die Zeilen natürlich keine perfekten Duplikate RowID Identitätsfeld.

Mein Tisch

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
War es hilfreich?

Lösung

Vorausgesetzt, es gibt keine Nullen, Sie GROUP BY die einzigartigen Spalten und SELECT Die MIN (or MAX) RowId als die zu behaltende Zeile.Dann löschen Sie einfach alles, was keine Zeilen-ID hatte:

DELETE FROM MyTable
LEFT OUTER JOIN (
   SELECT MIN(RowId) as RowId, Col1, Col2, Col3 
   FROM MyTable 
   GROUP BY Col1, Col2, Col3
) as KeepRows ON
   MyTable.RowId = KeepRows.RowId
WHERE
   KeepRows.RowId IS NULL

Falls Sie eine GUID anstelle einer Ganzzahl haben, können Sie diese ersetzen

MIN(RowId)

mit

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))

Andere Tipps

Eine andere Möglichkeit, dies zu tun, ist

; 

--Ensure that any immediately preceding statement is terminated with a semicolon above
WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3 
                                       ORDER BY ( SELECT 0)) RN
         FROM   #MyTable)
DELETE FROM cte
WHERE  RN > 1;

ich benutze ORDER BY (SELECT 0) oben, da es willkürlich ist, welche Zeile im Falle eines Unentschiedens beibehalten werden soll.

Um die neueste Version zu erhalten RowID Bestellung zum Beispiel, die Sie verwenden könnten ORDER BY RowID DESC

Ausführungspläne

Der Ausführungsplan hierfür ist häufig einfacher und effizienter als der in der akzeptierten Antwort, da kein Self-Join erforderlich ist.

Execution Plans

Dies ist jedoch nicht immer der Fall.Ein Ort, an dem die GROUP BY Die Lösung könnte in Situationen vorzuziehen sein, in denen a Hash-Aggregat würde einem Stream-Aggregat vorgezogen werden.

Der ROW_NUMBER Lösung wird immer ziemlich den gleichen Plan liefern, während die GROUP BY Strategie ist flexibler.

Execution Plans

Faktoren, die den Hash-Aggregat-Ansatz begünstigen könnten, wären:

  • Kein nützlicher Index für die Partitionierungsspalten
  • relativ weniger Gruppen mit relativ mehr Duplikaten in jeder Gruppe

In extremen Versionen dieses zweiten Falles (wenn es sehr wenige Gruppen mit jeweils vielen Duplikaten gibt) könnte man auch erwägen, die zu behaltenden Zeilen dann einfach in eine neue Tabelle einzufügen TRUNCATE-ingen Sie das Original und kopieren Sie es zurück, um die Protokollierung im Vergleich zum Löschen eines sehr großen Teils der Zeilen zu minimieren.

Es gibt einen guten Artikel darüber Entfernen von Duplikaten auf der Microsoft-Supportseite.Es ist ziemlich konservativ – Sie müssen alles in separaten Schritten erledigen –, aber es sollte bei großen Tischen gut funktionieren.

Ich habe dazu in der Vergangenheit Self-Joins verwendet, obwohl es wahrscheinlich mit einer HAVING-Klausel verschönert werden könnte:

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField

Die folgende Abfrage ist nützlich, um doppelte Zeilen zu löschen.Die Tabelle in diesem Beispiel hat ID als Identitätsspalte und die Spalten, die doppelte Daten enthalten Column1, Column2 Und Column3.

DELETE FROM TableName
WHERE  ID NOT IN (SELECT MAX(ID)
                  FROM   TableName
                  GROUP  BY Column1,
                            Column2,
                            Column3
                  /*Even if ID is not null-able SQL Server treats MAX(ID) as potentially
                    nullable. Because of semantics of NOT IN (NULL) including the clause
                    below can simplify the plan*/
                  HAVING MAX(ID) IS NOT NULL) 

Das folgende Skript zeigt die Verwendung von GROUP BY, HAVING, ORDER BY in einer Abfrage und gibt die Ergebnisse mit doppelter Spalte und deren Anzahl zurück.

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 
delete t1
from table t1, table t2
where t1.columnA = t2.columnA
and t1.rowid>t2.rowid

Postgres:

delete
from table t1
using table t2
where t1.columnA = t2.columnA
and t1.rowid > t2.rowid
DELETE LU 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY col1, col1, col3 
                   ORDER BY rowid DESC) [Row] 
        FROM   mytable) LU 
WHERE  [row] > 1 

Dadurch werden doppelte Zeilen gelöscht, mit Ausnahme der ersten Zeile

DELETE
FROM
    Mytable
WHERE
    RowID NOT IN (
        SELECT
            MIN(RowID)
        FROM
            Mytable
        GROUP BY
            Col1,
            Col2,
            Col3
    )

Verweisen (http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server)

Ich würde CTE zum Löschen doppelter Zeilen aus der SQL Server-Tabelle bevorzugen

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

durch Beibehaltung des Originals

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 beizubehalten

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)

Quick and Dirty zum Löschen exakter doppelter Zeilen (für kleine Tabellen):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;

Ich bevorzuge die Lösung „subquery\have count(*) > 1“ gegenüber dem „Inner Join“, weil sie meiner Meinung nach einfacher zu lesen ist und es sehr einfach ist, sie in eine SELECT-Anweisung umzuwandeln, um zu überprüfen, was gelöscht werden soll, bevor Sie sie ausführen.

--DELETE FROM table1 
--WHERE id IN ( 
     SELECT MIN(id) FROM table1 
     GROUP BY col1, col2, col3 
     -- could add a WHERE clause here to further filter
     HAVING count(*) > 1
--)

So rufen Sie doppelte Zeilen ab:

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

So löschen Sie die doppelten Zeilen:

DELETE users 
WHERE rowid NOT IN 
(SELECT MIN(rowid)
FROM users
GROUP BY name, email);      
SELECT  DISTINCT *
      INTO tempdb.dbo.tmpTable
FROM myTable

TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable

Ich dachte, ich würde meine Lösung teilen, da sie unter besonderen Umständen funktioniert.In meinem Fall hatte die Tabelle mit doppelten Werten keinen Fremdschlüssel (da die Werte aus einer anderen Datenbank dupliziert wurden).

begin transaction
-- create temp table with identical structure as source table
Select * Into #temp From tableName Where 1 = 2

-- insert distinct values into temp
insert into #temp 
select distinct * 
from  tableName

-- delete from source
delete from tableName 

-- insert into source from temp
insert into tableName 
select * 
from #temp

rollback transaction
-- if this works, change rollback to commit and execute again to keep you changes!!

PS:Wenn ich an solchen Dingen arbeite, verwende ich immer eine Transaktion. Dadurch wird nicht nur sichergestellt, dass alles als Ganzes ausgeführt wird, sondern ich kann auch testen, ohne etwas zu riskieren.Aber natürlich sollten Sie trotzdem ein Backup erstellen, nur um sicherzugehen ...

CTE verwenden.Die Idee besteht darin, eine oder mehrere Spalten zusammenzuführen, die einen doppelten Datensatz bilden, und dann die gewünschte Spalte zu entfernen:

;with cte as (
    select 
        min(PrimaryKey) as PrimaryKey
        UniqueColumn1,
        UniqueColumn2
    from dbo.DuplicatesTable 
    group by
        UniqueColumn1, UniqueColumn1
    having count(*) > 1
)
delete d
from dbo.DuplicatesTable d 
inner join cte on 
    d.PrimaryKey > cte.PrimaryKey and
    d.UniqueColumn1 = cte.UniqueColumn1 and 
    d.UniqueColumn2 = cte.UniqueColumn2;

Eine weitere einfache Lösung finden Sie unter dem eingefügten Link Hier.Dieses ist leicht zu verstehen und scheint bei den meisten ähnlichen Problemen wirksam zu sein.Es gilt zwar für SQL Server, aber das verwendete Konzept ist mehr als akzeptabel.

Hier sind die relevanten Teile der verlinkten Seite:

Betrachten Sie diese Daten:

EMPLOYEE_ID ATTENDANCE_DATE
A001    2011-01-01
A001    2011-01-01
A002    2011-01-01
A002    2011-01-01
A002    2011-01-01
A003    2011-01-01

Wie können wir also diese doppelten Daten löschen?

Fügen Sie zunächst eine Identitätsspalte in diese Tabelle ein, indem Sie den folgenden Code verwenden:

ALTER TABLE dbo.ATTENDANCE ADD AUTOID INT IDENTITY(1,1)  

Verwenden Sie den folgenden Code, um das Problem zu beheben:

DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _
    FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE) 

Diese Abfrage zeigte für mich eine sehr gute Leistung:

DELETE tbl
FROM
    MyTable tbl
WHERE
    EXISTS (
        SELECT
            *
        FROM
            MyTable tbl2
        WHERE
            tbl2.SameValue = tbl.SameValue
        AND tbl.IdUniqueValue < tbl2.IdUniqueValue
    )

Es löschte 1 Million Zeilen in etwas mehr als 30 Sekunden aus einer Tabelle mit 2 Millionen (50 % Duplikate).

Hier ist ein weiterer guter Artikel zum Thema Entfernen von Duplikaten.

Es wird erläutert, warum es schwierig ist:"SQL basiert auf der relationalen Algebra und Duplikate können in der relationalen Algebra nicht vorkommen, da Duplikate in einer Menge nicht zulässig sind."

Die temporäre Tabellenlösung und zwei MySQL-Beispiele.

In Zukunft werden Sie dies auf Datenbankebene oder aus Anwendungssicht verhindern.Ich würde die Datenbankebene vorschlagen, da Ihre Datenbank für die Aufrechterhaltung der referenziellen Integrität verantwortlich sein sollte. Entwickler werden nur Probleme verursachen ;)

Oh sicher.Verwenden Sie eine temporäre Tabelle.Wenn Sie eine einzelne, nicht sehr leistungsstarke Anweisung wünschen, die „funktioniert“, können Sie Folgendes tun:

DELETE FROM MyTable WHERE NOT RowID IN
    (SELECT 
        (SELECT TOP 1 RowID FROM MyTable mt2 
        WHERE mt2.Col1 = mt.Col1 
        AND mt2.Col2 = mt.Col2 
        AND mt2.Col3 = mt.Col3) 
    FROM MyTable mt)

Grundsätzlich findet die Unterauswahl für jede Zeile in der Tabelle die oberste Zeilen-ID aller Zeilen, die genau der betrachteten Zeile entsprechen.Am Ende erhalten Sie eine Liste von RowIDs, die die „ursprünglichen“, nicht duplizierten Zeilen darstellen.

Ich hatte eine Tabelle, in der ich nicht doppelte Zeilen beibehalten musste.Ich bin mir nicht sicher, was die Geschwindigkeit oder Effizienz angeht.

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )

Der andere Weg ist Erstelle eine neue Tabelle mit gleichen Feldern und mit eindeutigem Index.Dann Verschieben Sie alle Daten von der alten Tabelle in die neue Tabelle.SQL SERVER ignoriert automatisch (es gibt auch eine Option, was zu tun ist, wenn ein doppelter Wert vorhanden ist:ignorieren, unterbrechen oder etw.) doppelte Werte.Wir haben also dieselbe Tabelle ohne doppelte Zeilen. Wenn Sie keinen eindeutigen Index wünschen, können Sie ihn nach der Datenübertragung löschen.

Besonders für größere Tische Sie können DTS (SSIS-Paket zum Importieren/Exportieren von Daten) verwenden, um alle Daten schnell in Ihre neue, eindeutig indizierte Tabelle zu übertragen.Für 7 Millionen Zeilen dauert es nur wenige Minuten.

Benutze das

WITH tblTemp as
(
SELECT ROW_NUMBER() Over(PARTITION BY Name,Department ORDER BY Name)
   As RowNumber,* FROM <table_name>
)
DELETE FROM tblTemp where RowNumber >1

Mithilfe der folgenden Abfrage können wir doppelte Datensätze basierend auf einer einzelnen Spalte oder mehreren Spalten löschen.Die folgende Abfrage löscht basierend auf zwei Spalten.Tabellenname ist: testing und Spaltennamen empno,empname

DELETE FROM testing WHERE empno not IN (SELECT empno FROM (SELECT empno, ROW_NUMBER() OVER (PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
or empname not in
(select empname from (select empname,row_number() over(PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
  1. Erstellen Sie eine neue leere Tabelle mit derselben Struktur

  2. Führen Sie die Abfrage wie folgt aus

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
    
  3. Führen Sie dann diese Abfrage aus

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) = 1
    

Dies ist die einfachste Möglichkeit, doppelte Datensätze zu löschen

 DELETE FROM tblemp WHERE id IN 
 (
  SELECT MIN(id) FROM tblemp
   GROUP BY  title HAVING COUNT(id)>1
 )

http://askme.indianyouth.info/details/how-to-dumplicate-record-from-table-in-using-sql-105

Ich würde diesen Ansatz auch erwähnen, da er hilfreich sein kann und auf allen SQL-Servern funktioniert:Ziemlich oft gibt es nur ein bis zwei Duplikate, und IDs und Anzahl der Duplikate sind bekannt.In diesem Fall:

SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0

Von der Anwendungsebene (leider).Ich stimme zu, dass der richtige Weg, Duplikate zu verhindern, auf Datenbankebene durch die Verwendung eines eindeutigen Indexes liegt, aber in SQL Server 2005 darf ein Index nur 900 Byte groß sein, und mein Feld varchar(2048) macht das zunichte.

Ich weiß nicht, wie gut es funktionieren würde, aber ich denke, Sie könnten einen Auslöser schreiben, um dies durchzusetzen, selbst wenn Sie es nicht direkt mit einem Index tun könnten.Etwas wie:

-- given a table stories(story_id int not null primary key, story varchar(max) not null)
CREATE TRIGGER prevent_plagiarism 
ON stories 
after INSERT, UPDATE 
AS 
    DECLARE @cnt AS INT 

    SELECT @cnt = Count(*) 
    FROM   stories 
           INNER JOIN inserted 
                   ON ( stories.story = inserted.story 
                        AND stories.story_id != inserted.story_id ) 

    IF @cnt > 0 
      BEGIN 
          RAISERROR('plagiarism detected',16,1) 

          ROLLBACK TRANSACTION 
      END 

Außerdem klingt varchar(2048) für mich faul (manche Dinge im Leben sind 2048 Bytes groß, aber das ist ziemlich ungewöhnlich);sollte es wirklich nicht varchar(max) sein?

DELETE
FROM
    table_name T1
WHERE
    rowid > (
        SELECT
            min(rowid)
        FROM
            table_name T2
        WHERE
            T1.column_name = T2.column_name
    );
CREATE TABLE car(Id int identity(1,1), PersonId int, CarId int)

INSERT INTO car(PersonId,CarId)
VALUES(1,2),(1,3),(1,2),(2,4)

--SELECT * FROM car

;WITH CTE as(
SELECT ROW_NUMBER() over (PARTITION BY personid,carid order by personid,carid) as rn,Id,PersonID,CarId from car)

DELETE FROM car where Id in(SELECT Id FROM CTE WHERE rn>1)

Wenn Sie eine Vorschau der Zeilen anzeigen möchten, die Sie entfernen möchten, und die Kontrolle darüber behalten möchten, welche der doppelten Zeilen beibehalten werden sollen.Sehen http://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/

with MYCTE as (
  SELECT ROW_NUMBER() OVER (
    PARTITION BY DuplicateKey1
                ,DuplicateKey2 -- optional
    ORDER BY CreatedAt -- the first row among duplicates will be kept, other rows will be removed
  ) RN
  FROM MyTable
)
DELETE FROM MYCTE
WHERE RN > 1
DELETE 
FROM MyTable
WHERE NOT EXISTS (
              SELECT min(RowID)
              FROM Mytable
              WHERE (SELECT RowID 
                     FROM Mytable
                     GROUP BY Col1, Col2, Col3
                     ))
               );
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top