9.5 và mới hơn:
PostgreSQL 9.5 và hỗ trợ mới hơn INSERT ... ON CONFLICT (key) DO UPDATE
(và ON CONFLICT (key) DO NOTHING
), tức là nâng cấp.
So sánh với ON DUPLICATE KEY UPDATE
.
Giải thích nhanh.
Để biết cách sử dụng, hãy xem hướng dẫn - cụ thể là xung đột_action mệnh đề 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 cung cấp bên dưới, tính năng này hoạt động với nhiều hàng xung đột và không yêu cầu khóa riêng hoặc vòng lặp thử lại.
Cam kết bổ sung tính năng có ở đây và cuộc thảo luận xung quanh sự phát triển của nó ở đây.
Nếu bạn đang sử dụng phiên bả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ỳ UPSERT
tích hợp nào (hoặc MERGE
) cơ sở vật chất, và thực hiện nó một cách hiệu quả khi sử dụng đồng thời là rất khó.
Bài viết này thảo luận về vấn đề một cách 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
- 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 cảnh báo hàng riêng lẻ trong một vòng lặp 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 thủ tục hữu ích 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 chủng tộc, 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
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 trình kích hoạt hoặc khóa duy nhất phụ gây ra các vi phạm duy nhất.
Chiến lược này rất kém hiệu quả. Thay vào đó, bất cứ khi nào thực tế, bạn nên sắp xếp công việc và thực hiện nâng cấp hàng loạt như được mô tả bên dưới.
Nhiều giải pháp đã thử cho vấn đề này không xem xét đến việc khôi phục, vì vậy 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 kia gặp lỗi khóa trùng lặp và thực hiện UPDATE
thay thế. UPDATE
khối đang chờ INSERT
để khôi phục hoặc cam kết. Khi nó quay trở lại, UPDATE
kiểm tra lại điều kiện khớp với 0 hàng, vì vậy mặc dù UPDATE
cam kết rằng nó đã không thực sự làm được điều bạn mong đợi. Bạn phải kiểm tra số lượng hàng kết quả và thử lại nếu cần.
Một số giải pháp đã thử cũng không thể xem xét các chủng tộc CHỌN. Nếu bạn thử cách 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 số chế độ lỗi. Một là vấn đề đã được thảo luận với việc kiểm tra lại bản cập nhật. Khác là nơi cả UPDATE
đồng thời, khớp với hàng không và tiếp tục. Sau đó, cả hai đều thực hiện EXISTS
kiểm tra, điều này xảy ra trước đó INSERT
. Cả hai đều không có hàng, vì vậy cả hai đều thực hiện INSERT
. Một lỗi không thành công với lỗi khóa 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 chính trùng lặp hoặc các bản cập nhật bị mất bằng 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.
Vui lòng không áp dụng giải pháp của riêng bạn cho việc này. Giống như xếp hàng đợi tin nhắn, có thể là sai.
Nâng cấp hàng loạt có khóa
Đôi khi bạn muốn thực hiện nâng cấp hàng loạt, trong đó bạn có một tập dữ liệu mới mà bạn muốn hợp nhất vào một tập dữ liệu hiện có cũ hơn. Đây là rất lớn hiệu quả hơn các cảnh báo hàng riêng lẻ và nên được ưu tiên 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ộtTEMPORARY
bảng -
COPY
hoặc chèn hàng loạt dữ liệu mới vào bảng tạm thời -
LOCK
bảng mục tiêuIN EXCLUSIVE MODE
. Điều này cho phép các giao dịch khácSELECT
, nhưng không thực hiện bất kỳ thay đổi nào đối với bảng. -
Thực hiện
UPDATE ... FROM
trong số 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
INSERT
trong số các hàng chưa tồn tại trong bảng đích; -
COMMIT
, giải phóng khóa.
Ví dụ:đối với ví dụ được đưa ra trong câu hỏi, sử dụng INSERT
đa giá trị để đ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
- Trang wiki của UPSERT
- UPSERTisms trong Postgres
- Chèn vào bản cập nhật trùng lặp trong PostgreSQL?
- http://petereisentraut.blogspot.com/2010/05/merge-syntax.html
- Nâng cấp với một giao dịch
- CHỌN hoặc CHÈN trong một hàm có nguy cơ gặp phải các điều kiện về chủng tộc không?
- SQL
MERGE
trên wiki PostgreSQL - Cách thành ngữ nhất để triển khai UPSERT trong Postgresql ngày nay
Còn về MERGE
?
SQL-standard MERGE
thực sự có ngữ nghĩa đồng thời được xác định kém và không thích hợp để nâng cấp mà không khóa bảng trước.
Đó là một câu lệnh OLAP thực sự hữu ích để hợp nhất dữ liệu, nhưng nó thực sự không phải là một giải pháp hữu ích để nâng cấp an toàn đồng thời. Có rất nhiều lời khuyên cho những người đang sử dụng các DBMS khác để sử dụng MERGE
cho các cảnh báo, nhưng nó thực sự sai.
Các DB khác:
-
INSERT ... ON DUPLICATE KEY UPDATE
trong MySQL -
MERGE
từ MS SQL Server (nhưng xem ở trên vềMERGE
vấn đề) -
MERGE
từ Oracle (nhưng xem ở trên vềMERGE
vấn đề)