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

Postgres UPDATE с ORDER BY, как да го направя?

Доколкото знам, няма начин да се постигне това директно чрез UPDATE изявление; единственият начин да се гарантира редът на заключването е изрично придобиване на заключвания с SELECT ... ORDER BY ID FOR UPDATE , напр.:

UPDATE Balances
SET Balance = 0
WHERE ID IN (
  SELECT ID FROM Balances
  WHERE ID IN (SELECT ID FROM some_function())
  ORDER BY ID
  FOR UPDATE
)

Това има обратната страна на повтарянето на ID търсене на индекс на Balances маса. Във вашия прост пример можете да избегнете това натоварване, като извлечете адреса на физическия ред (представен от ctid системна колона ) по време на заявката за заключване и използване на това за задвижване на UPDATE :

UPDATE Balances
SET Balance = 0
WHERE ctid = ANY(ARRAY(
  SELECT ctid FROM Balances
  WHERE ID IN (SELECT ID FROM some_function())
  ORDER BY ID
  FOR UPDATE
))

(Бъдете внимателни, когато използвате ctid s, тъй като стойностите са преходни. Тук сме в безопасност, тъй като ключалките ще блокират всички промени.)

За съжаление плановият ще използва само ctid в тесен набор от случаи (можете да разберете дали работи, като потърсите възел „Tid Scan“ в EXPLAIN изход). За обработка на по-сложни заявки в рамките на една UPDATE изявление, напр. ако новият ви баланс е бил върнат от some_function() заедно с ID, ще трябва да се върнете към търсенето, базирано на ID:

UPDATE Balances
SET Balance = Locks.NewBalance
FROM (
  SELECT Balances.ID, some_function.NewBalance
  FROM Balances
  JOIN some_function() ON some_function.ID = Balances.ID
  ORDER BY Balances.ID
  FOR UPDATE
) Locks
WHERE Balances.ID = Locks.ID

Ако режийните разходи за производителност са проблем, ще трябва да прибягвате до използване на курсор, който ще изглежда по следния начин:

DO $$
DECLARE
  c CURSOR FOR
    SELECT Balances.ID, some_function.NewBalance
    FROM Balances
    JOIN some_function() ON some_function.ID = Balances.ID
    ORDER BY Balances.ID
    FOR UPDATE;
BEGIN
  FOR row IN c LOOP
    UPDATE Balances
    SET Balance = row.NewBalance
    WHERE CURRENT OF c;
  END LOOP;
END
$$


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. TransactionManagementError?

  2. Неочаквани резултати от SQL заявка с BETWEEN клеймца за време

  3. Как създавате Postgresql JSONB масив в индекса на масив?

  4. SQLAlchemy with_for_update заключване на ред не работи?

  5. датата на колоната не може да бъде преобразувана автоматично, за да въведете времево клеймо с часова зона django/postgres