Für eine Tabelle wie diese:
%Vor%Was wäre die korrekte Ein-Abfrage-Einfügung für die folgende Operation:
Geben Sie für einen Benutzer name
einen neuen Datensatz ein und geben Sie den neuen id
zurück. Aber wenn der name
bereits existiert, gib einfach id
zurück.
Ich kenne die neue Syntax in PostgreSQL 9.5 für ON CONFLICT(column) DO UPDATE/NOTHING
, aber ich kann nicht herausfinden, wie, wenn überhaupt, kann es helfen, da ich id
zurückgeben muss.
Es scheint, dass RETURNING id
und ON CONFLICT
nicht zusammen gehören.
Die UPSERT-Implementierung ist äußerst komplex, um vor gleichzeitigem Schreibzugriff zu schützen. Werfen Sie einen Blick auf dieses Postgres-Wiki , das während der anfänglichen Entwicklung als Protokoll diente. Die Postgres-Hacker entschieden, keine "ausgeschlossenen" Zeilen in die RETURNING
-Klausel für die erste Veröffentlichung in Postgres 9.5 aufzunehmen. Sie könnten etwas für die nächste Veröffentlichung einbauen.
Dies ist die entscheidende Aussage im Handbuch, um Ihre Situation zu erklären:
Die Syntax der
RETURNING
-Liste ist identisch mit der der Ausgabe Liste vonSELECT
. Nur Zeilen, die erfolgreich eingefügt oder aktualisiert wurden wird zurückgegeben. Beispiel: Eine Zeile wurde gesperrt, aber nicht aktualisiert weil eineON CONFLICT DO UPDATE ... WHERE
-Klauselbedingung nicht war zufrieden, die Zeile wird nicht zurückgegeben.
Fett Hervorhebung meins.
Für eine einzelne Zeile einfügen:
%Vor% Oder wrap in eine Funktion, um nur den neuen Namen einmal anzugeben. Wie hier demonstriert (beachte auch die Erklärung für LIMIT 1
):
Das mögliche Rennen: Eine gleichzeitige Transaktion könnte die bestehende Zeile zwischen dem INSERT
Versuch und dem SELECT
ändern / entfernen. Sehr unwahrscheinlich, aber möglich.
Wenn Sie (möglicherweise) keinen gleichzeitigen Schreibzugriff haben (oder einfach nicht interessiert), vereinfachen Sie:
%Vor%Um einen Satz von Zeilen einzufügen :
Für eine einzelne Zeile einfügen und keine Aktualisierung:
%Vor% Das Handbuch zu den primären und with
Unterabfragen Teile:
Die primäre Abfrage und die WITH-Abfragen werden alle gleichzeitig ( fiktiv ) ausgeführt
Obwohl mir das "selbe Schnappschuss" klingt, bin ich mir nicht sicher, da ich nicht weiß, was fiktiv in diesem Kontext bedeutet.
Aber gibt es auch :
Die Unteranweisungen in WITH werden gleichzeitig miteinander und mit der Hauptabfrage ausgeführt. Wenn Sie also datenverändernde Anweisungen in WITH verwenden, ist die Reihenfolge, in der die angegebenen Aktualisierungen tatsächlich stattfinden, nicht vorhersagbar. Alle Anweisungen werden mit demselben Snapshot ausgeführt
Wenn ich richtig verstehe, dass selbe Snapshot Bit eine Race-Bedingung verhindert. Aber ich bin mir auch nicht sicher, ob sich alle Anweisungen nur auf die Anweisungen in den with
Unterabfragen ohne die Hauptabfrage bezieht. Um Zweifel zu vermeiden, verschieben Sie die Auswahl in der vorherigen Abfrage in eine with
Unterabfrage:
Tags und Links sql postgresql concurrency upsert