私の知る限りでは、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
)
これはBalances
テーブルの上にID
インデックス検索を繰り返すの欠点を持っています。あなたの簡単な例では、あなたがロッククエリの実行中(ctid
system columnによって表される)物理行アドレスを取得し、それを駆動するために使用することで、このオーバーヘッドを回避することができUPDATE
:ctid
Sを使用した場合
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
を利用しません(あなたが探しているかどうかを知ることができます)。これは、値が一過性であるためです。 EXPLAIN
出力の「Tid Scan」ノード)。単一のUPDATE
ステートメント内でより複雑なクエリを処理する。新しいバランスはIDと一緒some_function()
で返されていた場合は、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
$$
構文... UPDATE ... FROM ...構文を試しましたか? –
はい、私はUPDATEを試しました.... FROM .... SELECT ... FOR UPDATEしかし、スタックトレースの変更はありません。実際に問題がより一般的になります。 – bbozo