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

Làm thế nào để bao gồm các hàng bị loại trừ trong QUAY LẠI khỏi CHÈN ... BẬT MÂU THUẪN

Lỗi bạn gặp phải:

Lệnh ON CONFLICT DO UPDATE không thể ảnh hưởng đến hàng lần thứ hai

cho biết bạn đang cố gắng nâng cấp cùng một hàng nhiều lần trong một lệnh. Nói cách khác:bạn có lỗi trên (name, url, email) trong VALUES của bạn danh sách. Gấp các bản sao (nếu đó là một tùy chọn) và nó sẽ hoạt động. Nhưng bạn sẽ phải quyết định chọn hàng nào từ mỗi tập hợp các lỗi.

INSERT INTO feeds_person (created, modified, name, url, email)
SELECT DISTINCT ON (name, url, email) *
FROM  (
   VALUES
   ('blah', 'blah', 'blah', 'blah', 'blah')
   -- ... more
   ) v(created, modified, name, url, email)  -- match column list
ON     CONFLICT (name, url, email) DO UPDATE
SET    url = feeds_person.url
RETURNING id;

Vì chúng tôi sử dụng VALUES độc lập bây giờ, bạn phải thêm các phôi kiểu rõ ràng cho các kiểu không mặc định. Như:

VALUES
    (timestamptz '2016-03-12 02:47:56+01'
   , timestamptz '2016-03-12 02:47:56+01'
   , 'n3', 'u3', 'e3')
   ...

timestamptz của bạn các cột cần truyền kiểu rõ ràng, trong khi các loại chuỗi có thể hoạt động với text mặc định . (Bạn vẫn có thể truyền tới varchar(n) ngay lập tức.)

Có nhiều cách để xác định hàng nào cần chọn từ mỗi tập hợp các lỗi:

  • Chọn hàng đầu tiên trong mỗi GROUP BY nhóm?

Bạn nói đúng, (hiện tại) không có cách nào để bị loại trừ các hàng trong RETURNING mệnh đề. Tôi trích dẫn Wiki của Postgres:

Lưu ý rằng RETURNING không hiển thị "EXCLUDED.* "bí danh từ UPDATE (chỉ là "TARGET.* chung chung "bí danh có thể nhìn thấy ở đây). Làm như vậy được cho là tạo ra sự mơ hồ khó chịu cho các trường hợp đơn giản, phổ biến [30] vì ít hoặc không có lợi. Tại một số thời điểm trong tương lai, chúng tôi có thể theo đuổi một cách hiển thị nếu RETURNING -các bộ giá trị dự án đã được chèn và cập nhật, nhưng điều này chắc chắn không cần phải đưa nó vào lần lặp cam kết đầu tiên của tính năng [31].

Tuy nhiên , bạn không nên cập nhật các hàng không được cập nhật. Các bản cập nhật trống gần như đắt tiền như các bản cập nhật thông thường - và có thể có các tác dụng phụ không mong muốn. Bạn không hoàn toàn cần UPSERT để bắt đầu, trường hợp của bạn trông giống như "CHỌN hoặc CHÈN". Có liên quan:

  • 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?

Một cách tốt hơn để chèn một tập hợp các hàng sẽ là với CTE sửa đổi dữ liệu:

WITH val AS (
   SELECT DISTINCT ON (name, url, email) *
   FROM  (
      VALUES 
      (timestamptz '2016-1-1 0:0+1', timestamptz '2016-1-1 0:0+1', 'n', 'u', 'e')
    , ('2016-03-12 02:47:56+01', '2016-03-12 02:47:56+01', 'n1', 'u3', 'e3')
      -- more (type cast only needed in 1st row)
      ) v(created, modified, name, url, email)
   )
, ins AS (
   INSERT INTO feeds_person (created, modified, name, url, email)
   SELECT created, modified, name, url, email FROM val
   ON     CONFLICT (name, url, email) DO NOTHING
   RETURNING id, name, url, email
   )
SELECT 'inserted' AS how, id FROM ins  -- inserted
UNION  ALL
SELECT 'selected' AS how, f.id         -- not inserted
FROM   val v
JOIN   feeds_person f USING (name, url, email);

Sự phức tạp tăng thêm sẽ phải trả cho các bảng lớn trong đó INSERT là quy tắc và SELECT ngoại lệ.

Ban đầu, tôi đã thêm một NOT EXISTS vị từ trên SELECT cuối cùng để ngăn chặn các bản sao trong kết quả. Nhưng điều đó là thừa. Tất cả các CTE của một truy vấn đều nhìn thấy cùng một ảnh chụp nhanh của các bảng. Tập hợp được trả về với ON CONFLICT (name, url, email) DO NOTHING loại trừ lẫn nhau đối với tập hợp được trả về sau INNER JOIN trên cùng các cột.

Thật không may, điều này cũng mở ra một cửa sổ nhỏ cho điều kiện cuộc đua . Nếu ...

  • một giao dịch đồng thời sẽ chèn các hàng xung đột
  • chưa cam kết
  • nhưng cuối cùng vẫn cam kết

... một số hàng có thể bị mất.

Bạn có thể chỉ cần INSERT .. ON CONFLICT DO NOTHING , theo sau là SELECT riêng biệt truy vấn cho tất cả các hàng - trong cùng một giao dịch để khắc phục điều này. Lần lượt mở ra một cửa sổ nhỏ khác cho điều kiện cuộc đua nếu các giao dịch đồng thời có thể cam kết ghi vào bảng giữa INSERTSELECT (ở mặc định READ COMMITTED mặc định mức cô lập). Có thể tránh được bằng REPEATABLE READ cách ly giao dịch (hoặc chặt chẽ hơn). Hoặc với một khóa ghi (có thể đắt tiền hoặc thậm chí không thể chấp nhận được) trên toàn bộ bảng. Bạn có thể thực hiện bất kỳ hành vi nào bạn cần, nhưng có thể có một cái giá phải trả.

Có liên quan:

  • Làm thế nào để sử dụng RETURNING với ON CONFLICT trong PostgreSQL?
  • Trả lại các hàng từ INSERT với ON CONFLICT mà không cần cập nhật



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ổ cắm miền PostgreSQL UNIX so với ổ cắm TCP

  2. Số lượng tham số cho phép tối đa cho mỗi loại nhà cung cấp cơ sở dữ liệu là bao nhiêu?

  3. Tối ưu hóa truy vấn với OFFSET trên bảng lớn

  4. Cắt bỏ khoảng trắng ở cuối với PostgreSQL

  5. Laravel:Lỗi [PDOException]:Không thể tìm thấy trình điều khiển trong PostgreSQL