Wenn Sie die bisherigen Antworten zusammenfassen, bereinigen und verbessern, kommen Sie zu dieser übergeordneten Frage:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Das ist viel schneller als jeder von ihnen. Nukes die Leistung der derzeit akzeptierten Antwort um Faktor 10 - 15 (in meinen Tests auf PostgreSQL 8.4 und 9.1).
Aber das ist noch lange nicht optimal. Verwenden Sie eine NOT EXISTS
(Anti-)Semi-Join für eine noch bessere Leistung. EXISTS
ist Standard-SQL, gibt es schon ewig (mindestens seit PostgreSQL 7.2, lange bevor diese Frage gestellt wurde) und passt perfekt zu den vorgestellten Anforderungen:
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<>Gefiedel ici_
Alte SQL-Fiedel
Eindeutiger Schlüssel zur Identifizierung der Zeile
Wenn Sie keinen Primär- oder eindeutigen Schlüssel für die Tabelle haben ( id
im Beispiel), können Sie durch die Systemspalte ersetzen ctid
für die Zwecke dieser Abfrage (aber nicht für andere Zwecke):
AND s1.ctid <> s.ctid
Jede Tabelle sollte einen Primärschlüssel haben. Fügen Sie einen hinzu, wenn Sie noch keinen haben. Ich empfehle einen <code>serial</code> oder ein <code>IDENTITY</code> Spalte in Postgres 10+.
Verwandt:
Wieso ist das schneller?
Die Unterabfrage in der EXISTS
anti-semi-join kann mit der Auswertung aufhören, sobald das erste Duplikat gefunden wurde (es macht keinen Sinn, weiter zu suchen). Bei einer Basistabelle mit wenigen Duplikaten ist dies nur geringfügig effizienter. Bei vielen Duplikaten wird dies zu Weg effizienter.
Leere Aktualisierungen ausschließen
Für Zeilen, die bereits über status = 'ACTIVE'
diese Aktualisierung würde nichts ändern, aber dennoch eine neue Zeilenversion zum vollen Preis einfügen (es gelten kleinere Ausnahmen). Normalerweise wollen Sie das nicht. Hinzufügen einer weiteren WHERE
Bedingung wie oben gezeigt, um dies zu vermeiden und es noch schneller zu machen:
Si status
ist definiert NOT NULL
können Sie auf vereinfachen:
AND status <> 'ACTIVE';
Der Datentyp der Spalte muss die <>
Betreiber. Einige Typen wie json
nicht. Siehe:
Geringfügiger Unterschied in der NULL-Behandlung
Diese Abfrage (im Gegensatz zu der derzeit akzeptierte Antwort von Joel ) behandelt NULL-Werte nicht als gleich. Die folgenden zwei Zeilen für (saleprice, saledate)
als "unterschiedlich" eingestuft werden (obwohl sie für das menschliche Auge identisch aussehen):
(123, NULL)
(123, NULL)
Geht auch in einem eindeutigen Index und fast überall sonst, da NULL-Werte nach dem SQL-Standard nicht gleichwertig sind. Siehe:
OTOH, GROUP BY
, DISTINCT
o DISTINCT ON ()
NULL-Werte als gleich behandeln. Verwenden Sie einen geeigneten Abfragestil, je nachdem, was Sie erreichen wollen. Sie können diese schnellere Abfrage immer noch verwenden mit IS NOT DISTINCT FROM
anstelle von =
für beliebige oder alle Vergleiche, um NULL-Vergleiche gleich zu machen. Mehr:
Wenn alle zu vergleichenden Spalten definiert sind NOT NULL
gibt es keinen Raum für Meinungsverschiedenheiten.