Se você reunir as respostas até o momento, limpar e melhorar, chegaria a esta consulta superior:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
O que é muito mais rápido que qualquer um deles. Nukes o desempenho da resposta atualmente aceita pelo fator 10 - 15 (nos meus testes no PostgreSQL 8.4 e 9.1).
Mas isso ainda está longe de ser o ideal. Use uma NOT EXISTS
(anti-) semi-junção para obter um desempenho ainda melhor. EXISTS
é SQL padrão, existe desde sempre (pelo menos desde o PostgreSQL 7.2, muito antes da pergunta) e se encaixa perfeitamente nos requisitos apresentados:
UPDATE sales s
SET status = 'ACTIVE'
WHERE NOT EXISTS (
SELECT FROM sales s1 -- SELECT list can be empty for EXISTS
WHERE s.saleprice = s1.saleprice
AND s.saledate = s1.saledate
AND s.id <> s1.id -- except for row itself
)
AND s.status IS DISTINCT FROM 'ACTIVE'; -- avoid empty updates. see below
db <> mexer aqui
Old SQL Fiddle
Chave exclusiva para identificar a linha
Se você não tiver uma chave primária ou exclusiva para a tabela ( id
no exemplo), poderá substituir a coluna do sistema ctid
pela finalidade desta consulta (mas não por outras finalidades):
AND s1.ctid <> s.ctid
Toda tabela deve ter uma chave primária. Adicione um se você ainda não o tiver. Sugiro uma serial
ou uma IDENTITY
coluna no Postgres 10+.
Relacionado:
Como isso é mais rápido?
A subconsulta na EXISTS
anti-semi-junção pode parar de avaliar assim que o primeiro dupe for encontrado (não faz sentido procurar mais). Para uma tabela base com poucas duplicatas, isso é levemente mais eficiente. Com muitas duplicatas, isso se torna muito mais eficiente.
Excluir atualizações vazias
Para linhas que já possuem status = 'ACTIVE'
essa atualização, isso não mudaria nada, mas ainda assim, insira uma nova versão de linha a custo total (pequenas exceções se aplicam). Normalmente, você não quer isso. Adicione outra WHERE
condição, como demonstrado acima, para evitar isso e torná-lo ainda mais rápido:
Se status
estiver definido NOT NULL
, você pode simplificar para:
AND status <> 'ACTIVE';
O tipo de dados da coluna deve suportar o <>
operador. Alguns tipos como json
não. Vejo:
Diferença sutil no manuseio de NULL
Esta consulta (diferente da resposta atualmente aceita por Joel ) não trata valores NULL como iguais. As duas linhas a seguir (saleprice, saledate)
serão classificadas como "distintas" (embora pareçam idênticas ao olho humano):
(123, NULL)
(123, NULL)
Também passa em um índice exclusivo e quase em qualquer outro lugar, já que os valores NULL não comparam iguais de acordo com o padrão SQL. Vejo:
OTOH, GROUP BY
, DISTINCT
ou DISTINCT ON ()
valores mimo nulo como igual. Use um estilo de consulta apropriado, dependendo do que você deseja alcançar. Você ainda pode usar essa consulta mais rápida com em IS NOT DISTINCT FROM
vez de=
em qualquer uma ou todas as comparações, para tornar a comparação NULL igual. Mais:
Se todas as colunas comparadas estiverem definidas NOT NULL
, não haverá espaço para discordância.