Vấn đề
Sách hướng dẫn giải thích:
RETURNING
tùy chọn mệnh đề nguyên nhânUPDATE
để tính toán và (các) giá trị trả về dựa trên mỗi hàng được cập nhật thực sự. Bất kỳ biểu thức nào sử dụng các cột của bảng và / hoặc các cột của các bảng khác được đề cập trongFROM
, có thể được tính toán. Các giá trị mới (sau cập nhật) của các cột trong bảng được sử dụng . Cú pháp củaRETURNING
danh sách giống với danh sách đầu ra củaSELECT
.
Tôi nhấn mạnh đậm. Không có cách nào để truy cập hàng cũ trong RETURNING
mệnh đề. Bạn có thể khắc phục hạn chế này bằng trình kích hoạt hoặc SELECT
riêng biệt trước đây UPDATE
được bao bọc trong một giao dịch hoặc được bao bọc trong một CTE như đã được nhận xét.
Tuy nhiên, những gì bạn đang cố gắng đạt được hoạt động hoàn toàn tốt nếu bạn tham gia vào một phiên bản khác của bảng trong FROM
mệnh đề:
Giải pháp không ghi đồng thời
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;
Lợi nhuận:
old_id | old_name | tbl_id | name
--------+----------+--------+---------
3 | Old Guy | 23 | New Guy
(Các) cột được sử dụng để tự tham gia phải UNIQUE NOT NULL
. Trong ví dụ đơn giản, WHERE
điều kiện nằm trên cùng một cột tbl_id
, nhưng đó chỉ là sự trùng hợp ngẫu nhiên. Hoạt động cho bất kỳ điều kiện.
Tôi đã thử nghiệm điều này với các phiên bản PostgreSQL từ 8.4 đến 13.
Nó khác với INSERT
:
- CHÈN VÀO ... TỪ CHỌN ... QUAY LẠI ánh xạ id
Các giải pháp có tải ghi đồng thời
Có nhiều cách khác nhau để tránh các điều kiện chạy đua với các thao tác ghi đồng thời trên các hàng giống nhau. (Lưu ý rằng các thao tác ghi đồng thời trên các hàng không liên quan hoàn toàn không có vấn đề gì.) Phương pháp đơn giản, chậm và chắc chắn (nhưng đắt tiền) là chạy giao dịch với SERIALIZABLE
mức độ cô lập:
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;
Nhưng đó có lẽ là quá mức cần thiết. Và bạn cần chuẩn bị để lặp lại thao tác trong trường hợp tuần tự hóa bị lỗi.
Đơn giản hơn và nhanh hơn (và cũng đáng tin cậy với tải ghi đồng thời) là một khóa rõ ràng trên một hàng sẽ được cập nhật:
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;
Lưu ý cách WHERE
điều kiện được chuyển đến truy vấn con (một lần nữa, có thể là bất kỳ điều gì ), và chỉ tự tham gia (trên UNIQUE NOT NULL
(các) cột) vẫn còn trong truy vấn bên ngoài. Điều này đảm bảo rằng chỉ các hàng bị khóa bởi SELECT
bên trong được xử lý. WHERE
các điều kiện có thể giải quyết thành một tập hợp hàng khác ngay sau đó.
Xem:
- CẬP NHẬT nguyên tử .. CHỌN trong Postgres
db <> fiddle here
Sqlfiddle cũ