Problém
Manuál vysvětluje:
Volitelné
RETURNINGklauzule způsobíUPDATEvypočí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 . SyntaxeRETURNINGseznam 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