Probleem
De handleiding legt uit:
De optionele
RETURNING
clausule veroorzaaktUPDATE
om te berekenen en waarde(n) terug te geven op basis van elke rij die daadwerkelijk is bijgewerkt. Elke expressie die de kolommen van de tabel en/of kolommen van andere tabellen gebruikt die worden genoemd inFROM
, kan worden berekend. De nieuwe (post-update) waarden van de kolommen van de tabel worden gebruikt . De syntaxis van deRETURNING
lijst is identiek aan die van de uitvoerlijst vanSELECT
.
Vetgedrukte nadruk van mij. Er is geen manier om toegang te krijgen tot de oude rij in een RETURNING
clausule. Je kunt deze beperking omzeilen met een trigger of een aparte SELECT
voor de UPDATE
verpakt in een transactie of verpakt in een CTE zoals werd opgemerkt.
Wat u echter probeert te bereiken werkt prima als u lid wordt van een andere instantie van de tabel in de FROM
clausule:
Oplossing zonder gelijktijdige schrijfbewerkingen
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;
Retourneren:
old_id | old_name | tbl_id | name
--------+----------+--------+---------
3 | Old Guy | 23 | New Guy
De kolom(men) die worden gebruikt om zelf lid te worden, moeten UNIQUE NOT NULL
zijn . In het eenvoudige voorbeeld, de WHERE
voorwaarde staat in dezelfde kolom tbl_id
, maar dat is gewoon toeval. Werkt voor elke voorwaarden.
Ik heb dit getest met PostgreSQL-versies van 8.4 tot 13.
Het is anders voor INSERT
:
- INSERT INTO ... FROM SELECT ... RETURNING id mappings
Oplossingen met gelijktijdige schrijfbelasting
Er zijn verschillende manieren om race-omstandigheden te vermijden met gelijktijdige schrijfbewerkingen op dezelfde rijen. (Merk op dat gelijktijdige schrijfbewerkingen op niet-gerelateerde rijen helemaal geen probleem zijn.) De eenvoudige, langzame en zekere (maar dure) methode is om de transactie uit te voeren met SERIALIZABLE
isolatieniveau:
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;
Maar dat is waarschijnlijk overdreven. En u moet voorbereid zijn om de handeling te herhalen in het geval van een serialisatiefout.
Eenvoudiger en sneller (en net zo betrouwbaar met gelijktijdige schrijfbelasting) is een expliciete vergrendeling op de één rij die moet worden bijgewerkt:
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;
Merk op hoe de WHERE
voorwaarde verplaatst naar de subquery (nogmaals, kan alles zijn ), en alleen de self-join (op UNIQUE NOT NULL
kolom(men)) blijft in de buitenste query. Dit garandeert dat alleen rijen worden vergrendeld door de binnenste SELECT
zijn verwerkt. De WHERE
omstandigheden kunnen even later worden omgezet in een andere reeks rijen.
Zie:
- Atomic UPDATE .. SELECT in Postgres
db<>viool hier
Oude sqlfiddle