Проблем
Ръководството обяснява:
Незадължителният
RETURNING
клауза причиняваUPDATE
за изчисляване и връщане на стойност(и) въз основа на всеки действително актуализиран ред. Всеки израз, използващ колоните на таблицата и/или колони от други таблици, споменати вFROM
, може да се изчисли. Използват се новите (след актуализация) стойности на колоните на таблицата . Синтаксисът наRETURNING
списъкът е идентичен с този на изходния списък наSELECT
.
Удебелен акцент мой. Няма начин за достъп до стария ред в RETURNING
клауза. Можете да заобиколите това ограничение с тригер или отделен SELECT
преди UPDATE
обвит в транзакция или обвит в CTE, както беше коментирано.
Това, което се опитвате да постигнете обаче,работи перфектно ако се присъедините към друг екземпляр на таблицата в FROM
клауза:
Решение без едновременни записи
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;
Връща:
old_id | old_name | tbl_id | name
--------+----------+--------+---------
3 | Old Guy | 23 | New Guy
Колоната(ите), използвани за самостоятелно присъединяване, трябва да бъде UNIQUE NOT NULL
. В простия пример, WHERE
условието е в същата колона tbl_id
, но това е просто съвпадение. Работи за всяко условия.
Тествах това с PostgreSQL версии от 8.4 до 13.
Различно е за INSERT
:
- INSERT IN... FROM SELECT ... ВРЪЩАНЕ на идентификационните съпоставяния
Решения с едновременно натоварване при запис
Има различни начини за избягване на условия на състезание с едновременни операции за запис на едни и същи редове. (Обърнете внимание, че едновременните операции по запис на несвързани редове изобщо не са проблем.) Простият, бавен и сигурен (но скъп) метод е да стартирате транзакцията с SERIALIZABLE
ниво на изолация:
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;
Но това вероятно е пресилено. И трябва да сте готови да повторите операцията в случай на неуспех на сериализацията.
По-просто и по-бързо (и също толкова надеждно с едновременно натоварване при запис) е изрично заключване на един ред за актуализиране:
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;
Обърнете внимание как WHERE
условието е преместено в подзаявката (отново може да бъде всичко ), и само самоприсъединяването (на UNIQUE NOT NULL
колона(и)) остава във външната заявка. Това гарантира, че само редовете са заключени от вътрешния SELECT
се обработват. WHERE
условията може да се разрешат до различен набор от редове момент по-късно.
Вижте:
- Атомна АКТУАЛИЗАЦИЯ .. ИЗБЕРЕТЕ в Postgres
db<>цигулка тук
Стар sqlfiddle