PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Geben Sie Spaltenwerte vor dem UPDATE nur mit SQL zurück

Problem

Das Handbuch erklärt:

Das optionale RETURNING -Klausel bewirkt UPDATE um Werte basierend auf jeder tatsächlich aktualisierten Zeile zu berechnen und zurückzugeben. Jeder Ausdruck, der die Spalten der Tabelle und/oder Spalten anderer Tabellen verwendet, die in FROM erwähnt werden , kann berechnet werden. Die neuen (nach der Aktualisierung) Werte der Tabellenspalten werden verwendet . Die Syntax von RETURNING Die Liste ist identisch mit der Ausgabeliste von SELECT .

Fette Hervorhebung von mir. Es gibt keine Möglichkeit, in einem RETURNING auf die alte Zeile zuzugreifen Klausel. Sie können diese Einschränkung mit einem Trigger oder einem separaten SELECT umgehen vorher das UPDATE verpackt in einer Transaktion oder verpackt in einem CTE, wie kommentiert wurde.

Das, was Sie erreichen möchten, funktioniert jedoch einwandfrei wenn Sie mit einer anderen Instanz der Tabelle in FROM verknüpfen Klausel:

Lösung ohne gleichzeitige Schreibvorgänge

UPDATE tbl x
SET    tbl_id = 23
     , name = 'New Guy'
FROM   tbl y                -- using the FROM clause
WHERE  x.tbl_id = y.tbl_id  -- must be UNIQUE NOT NULL
AND    x.tbl_id = 3
RETURNING y.tbl_id AS old_id, y.name AS old_name
        , x.tbl_id          , x.name;

Rückgabe:

 old_id | old_name | tbl_id |  name
--------+----------+--------+---------
  3     | Old Guy  | 23     | New Guy

Die für die Selbstverknüpfung verwendeten Spalten müssen UNIQUE NOT NULL sein . Im einfachen Beispiel das WHERE Bedingung befindet sich in derselben Spalte tbl_id , aber das ist nur Zufall. Funktioniert für alle Bedingungen.

Ich habe dies mit PostgreSQL-Versionen von 8.4 bis 13 getestet.

Anders bei INSERT :

  • INSERT INTO ... FROM SELECT ... RETURNING ID-Zuordnungen

Lösungen mit gleichzeitiger Schreiblast

Es gibt verschiedene Möglichkeiten, Race-Conditions mit gleichzeitigen Schreibvorgängen in denselben Zeilen zu vermeiden. (Beachten Sie, dass gleichzeitige Schreiboperationen auf nicht zusammenhängende Zeilen überhaupt kein Problem darstellen.) Die einfache, langsame und sichere (aber teure) Methode besteht darin, die Transaktion mit SERIALIZABLE auszuführen Isolationsstufe:

BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;

Aber das ist wohl übertrieben. Und Sie müssen darauf vorbereitet sein, den Vorgang im Falle eines Serialisierungsfehlers zu wiederholen.

Einfacher und schneller (und ebenso zuverlässig bei gleichzeitiger Schreiblast) ist eine explizite Sperre für one zu aktualisierende Zeile:

UPDATE tbl x
SET    tbl_id = 24
     , name = 'New Gal'
FROM  (SELECT tbl_id, name FROM tbl WHERE tbl_id = 4 FOR UPDATE) y 
WHERE  x.tbl_id = y.tbl_id
RETURNING y.tbl_id AS old_id, y.name AS old_name
        , x.tbl_id          , x.name;

Beachten Sie, wie der WHERE Bedingung in die Unterabfrage verschoben (kann wieder alles sein ) und nur der Self-Join (auf UNIQUE NOT NULL Spalte(n)) bleibt in der äußeren Abfrage. Dies garantiert, dass nur Zeilen durch das innere SELECT gesperrt werden verarbeitet werden. Das WHERE Bedingungen können einen Moment später in einen anderen Satz von Zeilen aufgelöst werden.

Siehe:

  • Atomic UPDATE .. SELECT in Postgres

db<>hier fummeln
Altes sqlfiddle