PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Връщане на стойности на колона преди АКТУАЛИЗАЦИЯ, използвайки само SQL

Проблем

Ръководството обяснява:

Незадължителният 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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Незадължителен аргумент във функцията PL/pgSQL

  2. Проблеми с createdb в postgres

  3. Как да сравним два масива и да изберете само несъвпадащите елементи в postgres

  4. Итерация над integer[] в PL/pgSQL

  5. Съветни заключване или NOWAIT, за да избегнете чакането на заключени редове?