Löschen doppelter Zeilen aus der SQLite-Datenbank

91

Ich habe eine riesige Tabelle - 36 Millionen Zeilen - in SQLite3. In dieser sehr großen Tabelle gibt es zwei Spalten:

  • hash - Text
  • d - Real

Einige der Zeilen sind Duplikate. Das heißt, beide hashund dhaben die gleichen Werte. Wenn zwei Hashes identisch sind, sind auch die Werte von identisch d. Zwei identische dimplizieren jedoch nicht zwei identische hash.

Ich möchte die doppelten Zeilen löschen. Ich habe keine Primärschlüsselspalte.

Was ist der schnellste Weg, dies zu tun?

Patches
quelle
Bitte platzieren Sie Antworten in Antwortblöcken. Später können Sie Ihre eigene Antwort akzeptieren. Siehe auch Wie funktioniert das Akzeptieren einer Antwort?
JWW

Antworten:

120

Sie benötigen eine Möglichkeit, die Zeilen zu unterscheiden. Basierend auf Ihrem Kommentar können Sie dafür die spezielle Rowid-Spalte verwenden .

So löschen Sie Duplikate von den niedrigsten zu halten rowidpro (hash,d):

delete   from YourTable
where    rowid not in
         (
         select  min(rowid)
         from    YourTable
         group by
                 hash
         ,       d
         )
Andomar
quelle
Mit SQLite können Sie keine Primärschlüsselspalte hinzufügen, oder?
Patches
sqlite> alter table dist add id integer primary key autoincrement; Error: Cannot add a PRIMARY KEY column
Patches
Interessant! Der Teil, den Sie brauchen, ist der autoincrement, funktioniert es, wenn Sie den primary keyTeil weglassen ?
Andomar
sqlite> alter table dist add id integer autoincrement; Error: near "autoincrement": syntax error Bearbeiten: SQLite hat ein Pseudo-Spaltentyp "rowid", der automatisch vorhanden ist. Kann ich das verwenden?
Patches
1
delete from dist where rowid not in (select max(rowid) from dist group by hash); Scheint den Trick zu machen! Vielen Dank.
Patches
5

Ich denke, am schnellsten wäre es, genau die Datenbank dafür zu verwenden: Fügen Sie eine neue Tabelle mit denselben Spalten hinzu, aber mit den richtigen Einschränkungen (ein eindeutiger Index für Hash / Real-Paar?), Durchlaufen Sie die ursprüngliche Tabelle und versuchen Sie, Datensätze einzufügen Die neue Tabelle ignoriert Fehler bei der Verletzung von Einschränkungen (dh setzt die Iteration fort, wenn Ausnahmen ausgelöst werden).

Löschen Sie dann die alte Tabelle und benennen Sie die neue in die alte um.

MaDa
quelle
Nicht so elegant wie das einfache Ändern der Tabelle, aber eine wirklich gute Sache an Ihrem Ansatz ist, dass Sie ihn so oft wiederholen können, wie Sie möchten, ohne die Quelldaten zu berühren / zu zerstören, bis Sie mit den Ergebnissen absolut zufrieden sind .
Adrian K
1

Wenn das Hinzufügen eines Primärschlüssels keine Option ist, besteht ein Ansatz darin, die Duplikate DISTINCT in einer temporären Tabelle zu speichern, alle duplizierten Datensätze aus der vorhandenen Tabelle zu löschen und die Datensätze dann wieder aus der temporären Tabelle in die ursprüngliche Tabelle einzufügen .

Zum Beispiel (geschrieben für SQL Server 2008, aber die Technik ist für jede Datenbank gleich):

DECLARE @original AS TABLE([hash] varchar(20), [d] float)
INSERT INTO @original VALUES('A', 1)
INSERT INTO @original VALUES('A', 2)
INSERT INTO @original VALUES('A', 1)
INSERT INTO @original VALUES('B', 1)
INSERT INTO @original VALUES('C', 1)
INSERT INTO @original VALUES('C', 1)

DECLARE @temp AS TABLE([hash] varchar(20), [d] float)
INSERT INTO @temp
SELECT [hash], [d] FROM @original 
GROUP BY [hash], [d]
HAVING COUNT(*) > 1

DELETE O
FROM @original O
JOIN @temp T ON T.[hash] = O.[hash] AND T.[d] = O.[d]

INSERT INTO @original
SELECT [hash], [d] FROM @temp

SELECT * FROM @original

Ich bin nicht sicher, ob SQLite eine Typfunktion hat ROW_NUMBER(), aber wenn dies der Fall ist, können Sie auch einige der hier aufgeführten Ansätze ausprobieren: Löschen Sie doppelte Datensätze aus einer SQL-Tabelle ohne Primärschlüssel

rsbarro
quelle
+1, nicht sicher, ob SQLite die delete <alias> from <table> <alias>Syntax unterstützt
Andomar