Se metti insieme le risposte finora, pulisci e migliora, arriveresti a questa domanda superiore:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Che è molto più veloce di entrambi. Nukes le prestazioni della risposta attualmente accettata dal fattore 10-15 (nei miei test su PostgreSQL 8.4 e 9.1).
Ma questo è ancora lungi dall'essere ottimale. Utilizzare un NOT EXISTS
semi-join (anti-) per prestazioni ancora migliori. EXISTS
è SQL standard, esiste da sempre (almeno da PostgreSQL 7.2, molto prima che questa domanda fosse posta) e si adatta perfettamente ai requisiti presentati:
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 <> violino qui
Vecchio SQL Fiddle
Chiave univoca per identificare la riga
Se non si dispone di una chiave primaria o unica per la tabella ( id
nell'esempio), è possibile sostituire con la colonna di sistema ctid
lo scopo di questa query (ma non per altri scopi):
AND s1.ctid <> s.ctid
Ogni tabella dovrebbe avere una chiave primaria. Aggiungine uno se non ne hai ancora uno. Suggerisco una serial
o una IDENTITY
colonna in Postgres 10+.
Relazionato:
Come è più veloce?
La sottoquery EXISTS
nell'anti-semi-join può interrompere la valutazione non appena viene trovato il primo duplicato (non è necessario guardare oltre). Per una tabella di base con pochi duplicati, questo è solo leggermente più efficiente. Con molti duplicati questo diventa molto più efficiente.
Escludere aggiornamenti vuoti
Per le righe che già dispongono di status = 'ACTIVE'
questo aggiornamento non cambierebbe nulla, ma inseriva comunque una nuova versione di riga a costo pieno (si applicano eccezioni minori). Normalmente, non lo vuoi. Aggiungi un'altra WHERE
condizione come mostrato sopra per evitarlo e renderlo ancora più veloce:
Se status
definito NOT NULL
, è possibile semplificare per:
AND status <> 'ACTIVE';
Il tipo di dati della colonna deve supportare l' <>
operatore. Alcuni tipi come json
no. Vedere:
Sottile differenza nella gestione NULL
Questa query (a differenza della risposta attualmente accettata da Joel ) non considera i valori NULL uguali. Le seguenti due righe per (saleprice, saledate)
si qualificherebbero "distinte" (sebbene sembrino identiche all'occhio umano):
(123, NULL)
(123, NULL)
Passa anche in un indice univoco e quasi ovunque, poiché i valori NULL non si equivalgono in base allo standard SQL. Vedere:
OTOH, GROUP BY
, DISTINCT
o DISTINCT ON ()
valori trattare NULL come uguali. Utilizzare uno stile di query appropriato a seconda di ciò che si desidera ottenere. Puoi comunque utilizzare questa query più veloce con IS NOT DISTINCT FROM
anziché =
per qualsiasi o tutti i confronti per rendere NULL il confronto uguale. Di Più:
Se vengono definite tutte le colonne da confrontare NOT NULL
, non c'è spazio per il disaccordo.