問題
マニュアルの説明:
オプションの
RETURNING句はUPDATEを引き起こします 実際に更新された各行に基づいて値を計算して返します。テーブルの列、および/またはFROMで言及されている他のテーブルの列を使用する式 、計算できます。 テーブルの列の新しい(更新後)値が使用されます 。RETURNINGの構文 listは、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 、しかしそれは単なる偶然です。 すべてで機能します 条件。
8.4から13までのPostgreSQLバージョンでこれをテストしました。
INSERTでは異なります :
- INSERT INTO ... FROM SELECT ...RETURNINGidマッピング
同時書き込みロードを使用したソリューション
同じ行での同時書き込み操作による競合状態を回避するには、さまざまな方法があります。 (無関係な行への同時書き込み操作はまったく問題ないことに注意してください。)単純で、遅く、確実な(しかし高価な)方法は、SERIALIZABLEを使用してトランザクションを実行することです。 分離レベル:
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;
しかし、それはおそらくやり過ぎです。また、シリアル化に失敗した場合に操作を繰り返す準備をする必要があります。
よりシンプルで高速(そして同時書き込みロードでも同様に信頼性が高い)は、 oneの明示的なロックです。 更新する行:
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 条件は、しばらくすると別の行のセットに解決される可能性があります。
参照:
- Atomic UPDATE..PostgresでのSELECT
db<>ここでフィドル
古いsqlfiddle