問題
マニュアルの説明:
オプションの
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