Na minha experiência (e como mostrado em muitos testes), NOT INcomo 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 ctidna 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-
INSERTsobreviventes.
- Reindexa
CREATEe chaves estrangeiras. As visualizações podem permanecer, elas não têm impacto no desempenho. Mais aqui ou aqui .