Persönlich habe ich eine "Regel" mit dem insert-Befehl verknüpft. Angenommen, Sie hatten eine "dns"-Tabelle, die dns-Treffer pro Kunde basierend auf einer bestimmten Zeitbasis aufzeichnet:
CREATE TABLE dns (
"time" timestamp without time zone NOT NULL,
customer_id integer NOT NULL,
hits integer
);
Sie möchten in der Lage sein, Zeilen mit aktualisierten Werten neu einzufügen oder sie zu erstellen, wenn sie noch nicht vorhanden sind. Dabei wird der customer_id und die Zeit als Schlüssel verwendet. Etwas in dieser Art:
CREATE RULE replace_dns AS
ON INSERT TO dns
WHERE (EXISTS (SELECT 1 FROM dns WHERE ((dns."time" = new."time")
AND (dns.customer_id = new.customer_id))))
DO INSTEAD UPDATE dns
SET hits = new.hits
WHERE ((dns."time" = new."time") AND (dns.customer_id = new.customer_id));
Update: Dies hat das Potenzial, zu scheitern, wenn gleichzeitige Einfügungen stattfinden, da es zu unique_violation Ausnahmen kommen kann. Die nicht abgeschlossene Transaktion wird jedoch fortgesetzt und erfolgreich abgeschlossen, und Sie müssen die abgebrochene Transaktion einfach wiederholen.
Jedoch, wenn ständig viele Einfügungen stattfinden, möchten Sie vielleicht eine Tabellensperre um die insert-Befehle herum setzen: SHARE ROW EXCLUSIVE-Sperren verhindern alle Operationen, die Zeilen in Ihrer Ziel-Tabelle einfügen, löschen oder aktualisieren könnten. Updates, die den eindeutigen Schlüssel nicht aktualisieren, sind jedoch sicher, also wenn keine Operation dies tun wird, verwenden Sie stattdessen Bezeichnungssperren.
Außerdem verwendet der COPY-Befehl keine REGELN, daher müssen Sie bei Einfügungen mit COPY statt dessen Trigger verwenden.
46 Stimmen
Jeder, der diese Frage findet, sollte den Artikel von Depesz "Warum ist upsert so kompliziert?" lesen. Es erklärt das Problem und mögliche Lösungen sehr gut.
9 Stimmen
UPSERT wird in Postgres 9.5 hinzugefügt: wiki.postgresql.org/wiki/…
6 Stimmen
@tommed - es wurde erledigt: stackoverflow.com/a/34639631/4418