Problem
Instrukcja wyjaśnia:
Opcjonalny RETURNING
klauzula powoduje UPDATE
do obliczenia i zwrócenia wartości na podstawie każdego faktycznie zaktualizowanego wiersza. Dowolne wyrażenie używające kolumn tabeli i/lub kolumn innych tabel wymienionych w FROM
, można obliczyć. Używane są nowe (po aktualizacji) wartości kolumn tabeli . Składnia RETURNING
lista jest identyczna z listą wyjściową SELECT
.
Moje odważne podkreślenie. Nie ma możliwości uzyskania dostępu do starego wiersza w RETURNING
klauzula. Możesz obejść to ograniczenie za pomocą wyzwalacza lub oddzielnego SELECT
przed UPDATE
opakowane w transakcję lub opakowane w CTE, jak skomentowano.
Jednak to, co próbujesz osiągnąć, działa doskonale jeśli dołączysz do innej instancji tabeli w FROM
klauzula:
Rozwiązanie bez równoczesnych zapisów
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;
Zwroty:
old_id | old_name | tbl_id | name
--------+----------+--------+---------
3 | Old Guy | 23 | New Guy
Kolumny używane do samodzielnego łączenia muszą być UNIQUE NOT NULL
. W prostym przykładzie WHERE
warunek znajduje się w tej samej kolumnie tbl_id
, ale to tylko zbieg okoliczności. Działa dla każdego warunki.
Testowałem to z wersjami PostgreSQL od 8.4 do 13.
Inaczej jest w przypadku INSERT
:
- WSTAW DO ... Z WYBIERZ ... POWRACAJĄC mapowania identyfikatorów
Rozwiązania z jednoczesnym ładowaniem zapisu
Istnieją różne sposoby uniknięcia sytuacji wyścigu przy współbieżnych operacjach zapisu w tych samych wierszach. (Zauważ, że współbieżne operacje zapisu na niepowiązanych wierszach nie stanowią żadnego problemu.) Prostą, powolną i pewną (ale kosztowną) metodą jest uruchomienie transakcji z SERIALIZABLE
poziom izolacji:
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;
Ale to prawdopodobnie przesada. Musisz być przygotowany na powtórzenie operacji w przypadku niepowodzenia serializacji.
Prostsze i szybsze (i tak samo niezawodne przy jednoczesnym ładowaniu zapisu) jest jawna blokada na jednym wiersz do aktualizacji:
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;
Zwróć uwagę, jak WHERE
warunek przeniesiony do podzapytania (ponownie, może być wszystkim ) i tylko samodołączenie (na UNIQUE NOT NULL
kolumna(y)) pozostaje w zewnętrznym zapytaniu. Gwarantuje to, że tylko wiersze zablokowane przez wewnętrzny SELECT
są przetwarzane. WHERE
warunki mogą chwilę później rozwiązać inny zestaw wierszy.
Zobacz:
- ATOMIC UPDATE .. SELECT w Postgresie
db<>graj tutaj
Stary sqlfiddle