9,5 và mới hơn:
PostgreQuery 9.5 và hỗ trợ mới hơn INSERT ... ON CONFLICT UPDATE
(và ON CONFLICT DO NOTHING
), tức là upert.
So sánh vớiON DUPLICATE KEY UPDATE
.
Giải thích nhanh .
Để sử dụng, hãy xem hướng dẫn - cụ thể là mệnh đề mâu thuẫn trong sơ đồ cú pháp và văn bản giải thích .
Không giống như các giải pháp cho 9,4 trở lên được đưa ra dưới đây, tính năng này hoạt động với nhiều hàng xung đột và nó không yêu cầu khóa độc quyền hoặc vòng lặp thử lại.
Cam kết thêm tính năng là ở đây và các cuộc thảo luận xung quanh sự phát triển của nó là ở đây .
Nếu bạn đang ở trên 9.5 và không cần phải tương thích ngược, bạn có thể ngừng đọc ngay bây giờ .
9,4 trở lên:
PostgreSQL không có bất kỳ tiện ích tích hợp UPSERT
(hoặc MERGE
) nào và việc thực hiện nó một cách hiệu quả khi đối mặt với việc sử dụng đồng thời là rất khó khăn.
Bài viết này thảo luận về vấn đề chi tiết hữu ích .
Nói chung, bạn phải chọn giữa hai tùy chọn:
- Các hoạt động chèn / cập nhật riêng lẻ trong một vòng lặp thử lại; hoặc là
- Khóa bảng và thực hiện hợp nhất hàng loạt
Vòng lặp thử lại hàng riêng lẻ
Sử dụng các đảo ngược hàng riêng lẻ trong một vòng thử lại là tùy chọn hợp lý nếu bạn muốn nhiều kết nối đồng thời cố gắng thực hiện chèn.
Tài liệu PostgreSQL chứa một quy trình hữu ích sẽ cho phép bạn thực hiện việc này trong một vòng lặp bên trong cơ sở dữ liệu . Nó bảo vệ chống lại các bản cập nhật bị mất và chèn các cuộc đua, không giống như hầu hết các giải pháp ngây thơ. Nó sẽ chỉ hoạt động trong READ COMMITTED
chế độ và chỉ an toàn nếu đó là điều duy nhất bạn làm trong giao dịch. Chức năng sẽ không hoạt động chính xác nếu kích hoạt hoặc khóa duy nhất thứ cấp gây ra vi phạm duy nhất.
Chiến lược này rất không hiệu quả. Bất cứ khi nào thực tế, bạn nên xếp hàng làm việc và thay thế hàng loạt như mô tả dưới đây.
Nhiều giải pháp đã cố gắng cho vấn đề này không xem xét các dự phòng, do đó chúng dẫn đến các bản cập nhật không đầy đủ. Hai giao dịch chạy đua với nhau; một trong số họ thành công INSERT
s; cái còn lại bị lỗi khóa trùng lặp và UPDATE
thay vào đó. Các UPDATE
khối đang chờ INSERT
để rollback hoặc cam kết. Khi nó quay trở lại, UPDATE
điều kiện kiểm tra lại khớp với các hàng bằng 0, do đó, mặc dù các UPDATE
cam kết nó thực sự không thực hiện được như bạn mong đợi. Bạn phải kiểm tra số lượng hàng kết quả và thử lại khi cần thiết.
Một số giải pháp đã cố gắng cũng không xem xét các cuộc đua CHỌN. Nếu bạn thử rõ ràng và đơn giản:
-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE.
BEGIN;
UPDATE testtable
SET somedata = 'blah'
WHERE id = 2;
-- Remember, this is WRONG. Do NOT COPY IT.
INSERT INTO testtable (id, somedata)
SELECT 2, 'blah'
WHERE NOT EXISTS (SELECT 1 FROM testtable WHERE testtable.id = 2);
COMMIT;
sau đó khi hai chạy cùng một lúc có một vài chế độ thất bại. Một là vấn đề đã được thảo luận với kiểm tra lại cập nhật. Một cái khác là cả hai UPDATE
cùng một lúc, khớp các hàng 0 và tiếp tục. Sau đó, cả hai đều làm EXISTS
bài kiểm tra, trong đó xảy ra trước khi các INSERT
. Cả hai đều nhận được hàng không, vì vậy cả hai đều làm INSERT
. Một lỗi với một lỗi chính trùng lặp.
Đây là lý do tại sao bạn cần một vòng lặp thử lại. Bạn có thể nghĩ rằng bạn có thể ngăn các lỗi khóa trùng lặp hoặc mất cập nhật với SQL thông minh, nhưng bạn không thể. Bạn cần kiểm tra số lượng hàng hoặc xử lý các lỗi chính trùng lặp (tùy thuộc vào cách tiếp cận đã chọn) và thử lại.
Xin đừng cuộn giải pháp của riêng bạn cho việc này. Giống như với việc xếp hàng tin nhắn, có lẽ sai.
Số lượng lớn có khóa
Đôi khi bạn muốn thực hiện một số lượng lớn, trong đó bạn có một bộ dữ liệu mới mà bạn muốn hợp nhất thành một bộ dữ liệu cũ hơn. Đây là bao la hiệu quả hơn upserts hàng cá nhân và nên được ưa thích bất cứ khi nào thực tế.
Trong trường hợp này, bạn thường làm theo quy trình sau:
CREATE
một cái TEMPORARY
bàn
COPY
hoặc chèn số lượng lớn dữ liệu mới vào bảng tạm thời
LOCK
bảng mục tiêu IN EXCLUSIVE MODE
. Điều này cho phép các giao dịch khác SELECT
, nhưng không thực hiện bất kỳ thay đổi nào đối với bảng.
Thực hiện một UPDATE ... FROM
trong các bản ghi hiện có bằng cách sử dụng các giá trị trong bảng tạm thời;
Thực hiện một INSERT
hàng không tồn tại trong bảng đích;
COMMIT
, phát hành khóa.
Ví dụ: đối với ví dụ được đưa ra trong câu hỏi, sử dụng đa giá trị INSERT
để điền vào bảng tạm thời:
BEGIN;
CREATE TEMPORARY TABLE newvals(id integer, somedata text);
INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');
LOCK TABLE testtable IN EXCLUSIVE MODE;
UPDATE testtable
SET somedata = newvals.somedata
FROM newvals
WHERE newvals.id = testtable.id;
INSERT INTO testtable
SELECT newvals.id, newvals.somedata
FROM newvals
LEFT OUTER JOIN testtable ON (testtable.id = newvals.id)
WHERE testtable.id IS NULL;
COMMIT;
Đọc liên quan
Thế còn MERGE
?
Tiêu chuẩn SQL MERGE
thực sự có ngữ nghĩa đồng thời được xác định kém và không phù hợp để nâng cấp mà không khóa bảng trước.
Đó là một tuyên bố OLAP thực sự hữu ích cho việc hợp nhất dữ liệu, nhưng thực sự nó không phải là một giải pháp hữu ích cho sự tăng cường an toàn đồng thời. Có rất nhiều lời khuyên cho những người sử dụng các DBMS khác để sử dụng MERGE
cho uperts, nhưng thực sự nó đã sai.
Các DB khác: