Wenn Sie die bisherigen Antworten zusammenstellen, aufräumen und verbessern, gelangen Sie zu dieser überlegenen Frage:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Welches ist viel schneller als beide. Nukes die Leistung der aktuell akzeptierten Antwort um den Faktor 10 - 15 (in meinen Tests auf PostgreSQL 8.4 und 9.1).
Dies ist jedoch noch lange nicht optimal. Verwenden Sie einen NOT EXISTS
(Anti-) Semi-Join für eine noch bessere Leistung. EXISTS
ist Standard-SQL, gibt es schon immer (zumindest 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 <> hier fummeln
Alte SQL-Geige
Eindeutiger Schlüssel zur Identifizierung der Zeile
Wenn Sie ( id
im Beispiel) keinen Primär- oder eindeutigen Schlüssel für die Tabelle haben , können Sie ctid
den Zweck dieser Abfrage durch die Systemspalte ersetzen (jedoch nicht für andere Zwecke):
AND s1.ctid <> s.ctid
Jede Tabelle sollte einen Primärschlüssel haben. Fügen Sie eine hinzu, wenn Sie noch keine hatten. Ich schlage ein serial
oder ein vorIDENTITY
Spalte in Postgres 10+ vor.
Verbunden:
Wie geht das schneller?
Die Unterabfrage im EXISTS
Anti-Semi-Join kann die Auswertung beenden, sobald der erste Betrüger gefunden wird (es macht keinen Sinn, weiter zu suchen). Für eine Basistabelle mit wenigen Duplikaten ist dies nur geringfügig effizienter. Mit vielen Duplikaten wird diese Art und Weise effizienter zu gestalten .
Leere Updates ausschließen
Für Zeilen, die status = 'ACTIVE'
dieses Update bereits haben, würde sich nichts ändern, aber dennoch eine neue Zeilenversion zum vollen Preis einfügen (kleinere Ausnahmen gelten). Normalerweise willst du das nicht. Fügen Sie eine weitere WHERE
Bedingung wie oben gezeigt hinzu, um dies zu vermeiden und noch schneller zu machen:
Wenn status
definiert ist NOT NULL
, können Sie vereinfachen, um:
AND status <> 'ACTIVE';
Subtiler Unterschied in der NULL-Behandlung
Diese Abfrage behandelt (im Gegensatz zu der derzeit von Joel akzeptierten Antwort ) NULL-Werte nicht als gleich. Die folgenden zwei Zeilen für (saleprice, saledate)
würden als "verschieden" qualifiziert (obwohl sie mit dem menschlichen Auge identisch aussehen):
(123, NULL)
(123, NULL)
Übergibt auch einen eindeutigen Index und fast überall sonst, da NULL-Werte gemäß dem SQL-Standard nicht gleich sind. Sehen:
OTOH, GROUP BY
, DISTINCT
oder DISTINCT ON ()
treat NULL - Werte als gleich. Verwenden Sie einen geeigneten Abfragestil, je nachdem, was Sie erreichen möchten. Sie können diese schnellere Abfrage weiterhin mit IS NOT DISTINCT FROM
anstelle von =
für einen oder alle Vergleiche verwenden, um den NULL-Vergleich gleich zu machen. Mehr:
Wenn alle verglichenen Spalten definiert sind NOT NULL
, gibt es keinen Raum für Meinungsverschiedenheiten.