D'après mon expérience (et comme le montrent de nombreux tests) NOT IN
comme démontré par @gsiems est plutôt lent et évolue terriblement. L'inverse IN
est généralement plus rapide (où vous pouvez reformuler de cette façon, comme dans ce cas), mais cette requête avec EXISTS
(faire exactement ce que vous avez demandé) devrait être encore beaucoup plus rapide - avec de grands tableaux par ordre de grandeur :
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
);
Supprime chaque ligne où une autre ligne avec la même (tag_id, question_id)
et une plus petite ctid
existe . (Conserve effectivement la première instance selon l'ordre physique des tuples.) En utilisant ctid
en l'absence d'une meilleure alternative, votre table ne semble pas avoir de PK ou toute autre (ensemble de) colonne (s) unique (s).
ctid
est l'identifiant de tuple interne présent dans chaque ligne et nécessairement unique. Lectures complémentaires:
Tester
J'ai exécuté un cas de test avec ce tableau correspondant à votre question et 100 000 lignes:
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;
Les index n'aident pas dans ce cas.
Résultats
NOT IN
Le SQLfiddle expire .
J'ai essayé la même chose localement, mais je l'ai également annulée après plusieurs minutes.
EXISTS
Termine en une demi-seconde dans ce SQLfiddle .
Alternatives
Si vous allez supprimer la plupart des lignes , il sera plus rapide de sélectionner les survivants dans une autre table, de supprimer l'original et de renommer la table des survivants. Attention, cela a des implications si vous avez des clés de vue ou étrangères (ou d'autres dépendances) définies sur l'original.
Si vous avez des dépendances et que vous souhaitez les conserver, vous pouvez:
- Supprimez toutes les clés et index étrangers - pour des performances.
SELECT
survivants à une table temporaire.
TRUNCATE
l'original.
- Re-
INSERT
survivants.
- Réindexation
CREATE
et clés étrangères. Les vues peuvent simplement rester, elles n'ont aucun impact sur les performances. Plus ici ou ici .
Vous pouvez utiliser le ctid pour accomplir cela. Par exemple:
Créez un tableau avec des doublons:
Sélectionnez les données en double:
Supprimez les données en double:
Dans votre cas, les éléments suivants devraient fonctionner:
la source
ctid
? Merci.ctid
veut dire?