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