Nach meiner Erfahrung (und wie in vielen Tests gezeigt) , NOT IN
wie durch @gsiems gezeigt ist eher langsam und Skalen schrecklich. Die Umkehrung IN
ist normalerweise schneller (wo Sie wie in diesem Fall auf diese Weise umformulieren können), aber diese Abfrage mit EXISTS
(genau das tun, was Sie gefragt haben) sollte noch viel schneller sein - mit großen Tabellen um Größenordnungen :
DELETE FROM questions_tags q
WHERE EXISTS (
SELECT FROM questions_tags q1
WHERE q1.ctid < q.ctid
AND q1.question_id = q.question_id
AND q1.tag_id = q.tag_id
);
Löscht jede Zeile, in der eine andere Zeile mit derselben (tag_id, question_id)
und einer kleineren ctid
vorhanden ist . (Hält die erste Instanz effektiv gemäß der physischen Reihenfolge der Tupel.) Verwendenctid
In Ermangelung einer besseren Alternative scheint Ihre Tabelle keine PK oder andere eindeutige (Satz von) Spalten zu haben.
ctid
ist die interne Tupelkennung, die in jeder Zeile vorhanden und notwendigerweise eindeutig ist. Weiterführende Literatur:
Prüfung
Ich habe einen Testfall mit dieser Tabelle ausgeführt, die auf Ihre Frage und 100.000 Zeilen abgestimmt ist:
CREATE TABLE questions_tags(
question_id integer NOT NULL
, tag_id integer NOT NULL
);
INSERT INTO questions_tags (question_id, tag_id)
SELECT (random()* 100)::int, (random()* 100)::int
FROM generate_series(1, 100000);
ANALYZE questions_tags;
Indizes helfen in diesem Fall nicht.
Ergebnisse
NOT IN
Die SQLfiddle läuft ab.
Versuchte das gleiche vor Ort, aber ich stornierte es auch nach einigen Minuten.
EXISTS
Beendet diese SQLfiddle in einer halben Sekunde .
Alternativen
Wenn Sie die meisten Zeilen löschen möchten , können Sie die Überlebenden schneller in einer anderen Tabelle auswählen, das Original löschen und die Tabelle der Überlebenden umbenennen. Vorsicht, dies hat Auswirkungen, wenn auf dem Original Ansichts- oder Fremdschlüssel (oder andere Abhängigkeiten) definiert sind.
Wenn Sie Abhängigkeiten haben und diese beibehalten möchten, können Sie:
- Löschen Sie alle Fremdschlüssel und Indizes - für die Leistung.
SELECT
Überlebende an einen temporären Tisch.
TRUNCATE
das Original.
- Re-
INSERT
Überlebenden.
- Re-
CREATE
Indizes und Fremdschlüssel. Ansichten können einfach bleiben, sie haben keinen Einfluss auf die Leistung. Mehr hier oder hier .