Na minha experiência (e como mostrado em muitos testes), NOT IN
como demonstrado por @gsiems, é bastante lento e dimensiona terrivelmente. O inverso IN
é geralmente mais rápido (onde você pode reformular dessa maneira, como neste caso), mas essa consulta com EXISTS
(fazendo exatamente o que você pediu) deve ser muito mais rápida ainda - com grandes tabelas por ordem de magnitude :
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
);
Exclui todas as linhas em que existe outra linha com a mesma (tag_id, question_id)
e menorctid
. (Mantém efetivamente a primeira instância de acordo com a ordem física das tuplas.) Usando ctid
na ausência de uma alternativa melhor, sua tabela parece não ter uma PK ou qualquer outra (s) coluna (s) exclusiva (s).
ctid
é o identificador de tupla interno presente em todas as linhas e necessariamente exclusivo. Leitura adicional:
Teste
Executei um caso de teste com esta tabela correspondente à sua pergunta e 100 mil linhas:
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;
Os índices não ajudam nesse caso.
Resultados
NOT IN
O tempo limite do SQLfiddle .
Tentei o mesmo localmente, mas também o cancelei depois de alguns minutos.
EXISTS
Termina em meio segundo neste SQLfiddle .
Alternativas
Se você deseja excluir a maioria das linhas , será mais rápido selecionar os sobreviventes em outra tabela, soltar o original e renomear a tabela de sobreviventes. Cuidado, isso tem implicações se você tiver chaves de exibição ou estrangeiras (ou outras dependências) definidas no original.
Se você possui dependências e deseja mantê-las, pode:
- Solte todas as chaves e índices estrangeiros - para obter desempenho.
SELECT
sobreviventes para uma mesa temporária.
TRUNCATE
o original.
- Re-
INSERT
sobreviventes.
- Reindexa
CREATE
e chaves estrangeiras. As visualizações podem permanecer, elas não têm impacto no desempenho. Mais aqui ou aqui .