PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Làm cách nào để UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) trong PostgreSQL?

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ột TEMPORARY 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ê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 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 đề)


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. UUID hoặc SEQUENCE cho khóa chính?

  2. Các hàm toán học PostgreSQL

  3. Psycopg2, Postgresql, Python:Cách nhanh nhất để chèn hàng loạt

  4. Rails:Cài đặt PG gem trên OS X - không tạo được phần mở rộng gốc

  5. PostgreSQL, kéo và hoán đổi