sql >> Database teknologi >  >> RDS >> PostgreSQL

Returner pre-UPDATE kolonneværdier kun ved brug af SQL

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



  1. Sådan finder du duplikerede poster i MySQL

  2. PreparedStatement og setTimestamp i oracle jdbc

  3. SQL CASE-erklæring

  4. Send ordbog<string,int> til Stored Procedure T-SQL