sql >> Databáze >  >> RDS >> PostgreSQL

Vraťte hodnoty sloupců před aktualizací pouze pomocí SQL

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 v FROM , lze vypočítat. Použijí se nové (po aktualizaci) hodnoty sloupců tabulky . Syntaxe RETURNING seznam je totožný s výstupním seznamem SELECT .

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



  1. rozdíl mezi plánem vysvětlit a plánem provedení

  2. Chyba PHP (MySQL):Upozornění:mysql_num_rows() očekává, že parametr 1 bude zdrojem

  3. Zkontrolujte omezení v SQL

  4. Java:Vložte více řádků do MySQL pomocí PreparedStatement