Problém
Manuál vysvětluje:
Volitelné
RETURNING
klauzule způsobíUPDATE
vypočítat a návratovou hodnotu (hodnoty) na základě každého skutečně aktualizovaného řádku. Jakýkoli výraz používající sloupce tabulky a/nebo sloupce jiných tabulek uvedených vFROM
, lze vypočítat. Použijí se nové (po aktualizaci) hodnoty sloupců tabulky . SyntaxeRETURNING
seznam je totožný s výstupním seznamemSELECT
.
Odvážný důraz můj. Neexistuje žádný způsob, jak získat přístup ke starému řádku v RETURNING
doložka. Toto omezení můžete obejít pomocí spouštěče nebo samostatného SELECT
před UPDATE
zabalené do transakce nebo zabalené do CTE, jak bylo komentováno.
To, čeho se snažíte dosáhnout, však funguje naprosto v pořádku pokud se připojíte k jiné instanci tabulky v FROM
klauzule:
Řešení bez souběžných zápisů
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;
Vrátí:
old_id | old_name | tbl_id | name
--------+----------+--------+---------
3 | Old Guy | 23 | New Guy
Sloupce použité k samostatnému připojení musí být UNIQUE NOT NULL
. V jednoduchém příkladu WHERE
podmínka je ve stejném sloupci tbl_id
, ale to je jen náhoda. Funguje pro jakékoli podmínky.
Testoval jsem to s verzemi PostgreSQL od 8.4 do 13.
U INSERT
je to jiné :
- VLOŽTE DO... Z VÝBĚRU... VRACÍCH mapování ID
Řešení se současným zatížením zápisu
Existují různé způsoby, jak se vyhnout sporům se souběžnými operacemi zápisu na stejné řádky. (Všimněte si, že souběžné operace zápisu na nesouvisejících řádcích nejsou vůbec žádný problém.) Jednoduchá, pomalá a jistá (ale drahá) metoda je spustit transakci pomocí SERIALIZABLE
úroveň izolace:
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;
Ale to je asi přehnané. A musíte být připraveni operaci zopakovat v případě selhání serializace.
Jednodušší a rychlejší (a stejně spolehlivé při souběžném zápisu) je explicitní zámek na jednu řádek k aktualizaci:
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;
Všimněte si, jak WHERE
podmínka přesunuta do poddotazu (opět může být cokoli ) a pouze vlastní spojení (na UNIQUE NOT NULL
sloupec(s)) zůstane ve vnějším dotazu. To zaručuje, že pouze řádky jsou uzamčeny vnitřním SELECT
jsou zpracovány. WHERE
podmínky se mohou o chvíli později změnit na jinou sadu řádků.
Viz:
- ATOMOVÁ AKTUALIZACE .. SELECT v Postgres
db<>zde hrajte
Starý sqlfiddle