Problem
Manualen forklarer:
Den valgfri RETURNING klausul forårsager UPDATE at beregne og returnere værdi(er) baseret på hver række faktisk opdateret. Ethvert udtryk, der bruger tabellens kolonner og/eller kolonner i andre tabeller nævnt i FROM , kan beregnes. De nye (efter-opdatering) værdier i tabellens kolonner bruges . Syntaksen for RETURNING listen er identisk med den for outputlisten for SELECT .
Fed fremhævelse min. Der er ingen måde at få adgang til den gamle række i en RETURNING klausul. Du kan omgå denne begrænsning med en trigger eller en separat SELECT før UPDATE pakket ind i en transaktion eller pakket ind i en CTE, som blev kommenteret.
Det du forsøger at opnå fungerer dog helt fint hvis du tilslutter dig en anden forekomst af tabellen i FROM klausul:
Løsning uden samtidige skrivninger
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;
Returnerer:
old_id | old_name | tbl_id | name
--------+----------+--------+---------
3 | Old Guy | 23 | New Guy
Den eller de kolonner, der bruges til selv-join, skal være UNIQUE NOT NULL . I det simple eksempel er WHERE betingelse er i den samme kolonne tbl_id , men det er bare tilfældigt. Virker for alle betingelser.
Jeg testede dette med PostgreSQL-versioner fra 8.4 til 13.
Det er anderledes for INSERT :
- INSERT INTO ... FRA SELECT ... RETURNERER id-tilknytninger
Løsninger med samtidig skrivebelastning
Der er forskellige måder at undgå løbsforhold med samtidige skriveoperationer på de samme rækker. (Bemærk, at samtidige skriveoperationer på ikke-relaterede rækker overhovedet ikke er noget problem.) Den enkle, langsomme og sikre (men dyre) metode er at køre transaktionen med SERIALIZABLE isolationsniveau:
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;
Men det er nok overkill. Og du skal være forberedt på at gentage handlingen i tilfælde af en serialiseringsfejl.
Enklere og hurtigere (og lige så pålidelig med samtidig skrivebelastning) er en eksplicit lås på en række, der skal opdateres:
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;
Bemærk hvordan WHERE betingelse flyttet til underforespørgslen (kan igen være hvad som helst ), og kun selv-join (på UNIQUE NOT NULL kolonne(r)) forbliver i den ydre forespørgsel. Dette garanterer, at kun rækker er låst af den indre SELECT behandles. WHERE betingelser kan løses til et andet sæt rækker et øjeblik senere.
Se:
- Atomic OPDATERING .. VÆLG i Postgres
db<>spil her
Gamle sqlfiddle