Si vous réunissez les réponses jusqu'à présent, nettoyez et améliorez, vous arriverez à cette requête supérieure:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Ce qui est beaucoup plus rapide que l'un ou l'autre. Détruit les performances de la réponse actuellement acceptée par le facteur 10 - 15 (dans mes tests sur PostgreSQL 8.4 et 9.1).
Mais c'est encore loin d'être optimal. Utilisez une NOT EXISTS
(anti) semi-jointure pour des performances encore meilleures. EXISTS
est du SQL standard, existe depuis toujours (au moins depuis PostgreSQL 7.2, bien avant que cette question ne soit posée) et correspond parfaitement aux exigences présentées:
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 <> violon ici
Old SQL Fiddle
Clé unique pour identifier la ligne
Si vous n'avez pas de clé primaire ou unique pour la table ( id
dans l'exemple), vous pouvez remplacer par la colonne système ctid
aux fins de cette requête (mais pas à d'autres fins):
AND s1.ctid <> s.ctid
Chaque table doit avoir une clé primaire. Ajoutez-en un si vous n'en avez pas encore. Je suggère une serial
ou une IDENTITY
colonne dans Postgres 10+.
En relation:
Comment est-ce plus rapide?
La sous-requête dans l' EXISTS
anti-semi-jointure peut cesser d'être évaluée dès que la première dupe est trouvée (inutile de chercher plus loin). Pour une table de base avec peu de doublons, cela n'est que légèrement plus efficace. Avec beaucoup de doublons, cela devient beaucoup plus efficace.
Exclure les mises à jour vides
Pour les lignes qui ont déjà status = 'ACTIVE'
cette mise à jour, cela ne changera rien, mais insérez toujours une nouvelle version de ligne à plein coût (des exceptions mineures s'appliquent). Normalement, vous ne le souhaitez pas. Ajoutez une autre WHERE
condition comme illustré ci-dessus pour éviter cela et le rendre encore plus rapide:
Si status
est défini NOT NULL
, vous pouvez simplifier pour:
AND status <> 'ACTIVE';
Le type de données de la colonne doit prendre en charge l' <>
opérateur. Certains types aiment json
pas. Voir:
Différence subtile dans la gestion NULL
Cette requête (contrairement à la réponse actuellement acceptée par Joel ) ne traite pas les valeurs NULL comme égales. Les deux lignes suivantes pour (saleprice, saledate)
seraient qualifiées de "distinctes" (bien qu'elles semblent identiques à l'œil humain):
(123, NULL)
(123, NULL)
Passe également dans un index unique et presque partout ailleurs, car les valeurs NULL ne sont pas comparables égales selon la norme SQL. Voir:
OTOH, GROUP BY
, DISTINCT
ou DISTINCT ON ()
traiter les valeurs NULL comme égales. Utilisez un style de requête approprié en fonction de ce que vous souhaitez réaliser. Vous pouvez toujours utiliser cette requête plus rapide avec IS NOT DISTINCT FROM
au lieu de =
pour une ou toutes les comparaisons pour que la comparaison NULL soit égale. Plus:
Si toutes les colonnes comparées sont définies NOT NULL
, il n'y a pas de place pour le désaccord.