Làm thế nào để có được ID của hàng xung đột trong upert?


18

Tôi có một bảng tagcó 2 cột: id(uuid) và name(văn bản). Bây giờ tôi muốn chèn một thẻ mới vào bảng, nhưng nếu thẻ đã tồn tại, tôi chỉ muốn lấy idbản ghi hiện có.

Tôi giả sử tôi chỉ có thể sử dụng ON CONFLICT DO NOTHINGkết hợp với RETURNING "id":

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT DO NOTHING
RETURNING "id";

Nhưng điều này trả về một tập kết quả trống, nếu thẻ có tên "foo" đã tồn tại.

Sau đó tôi đã thay đổi truy vấn để sử dụng DO UPDATEmệnh đề noop :

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT ("name") DO UPDATE SET "name" = 'foo'
RETURNING "id";

Điều này hoạt động như dự định, nhưng hơi khó hiểu, vì tôi chỉ đặt tên cho giá trị đã tồn tại.

Đây có phải là cách để giải quyết vấn đề này hay có một cách tiếp cận đơn giản hơn mà tôi đang thiếu?


bạn đã thử returning excluded.idchưa
a_horse_with_no_name 16/2/2016

@a_horse_with_no_name Điều đó chỉ mang lại cho tôi ERROR: missing FROM-clause entry for table "excluded"khi sử dụng DO NOTHING.
Der Hochstapler 16/2/2016

Câu trả lời:


8

Điều này sẽ hoạt động (theo như tôi đã thử nghiệm) trong cả 3 trường hợp, nếu các giá trị được chèn là hoàn toàn mới hoặc tất cả đã có trong bảng hoặc kết hợp:

WITH
  val (name) AS
    ( VALUES                          -- rows to be inserted
        ('foo'),
        ('bar'),
        ('zzz')
    ),
  ins AS
    ( INSERT INTO
        tag (name)
      SELECT name FROM val
      ON CONFLICT (name) DO NOTHING
      RETURNING id, name              -- only the inserted ones
    )
SELECT COALESCE(ins.id, tag.id) AS id, 
       val.name
FROM val
  LEFT JOIN ins ON ins.name = val.name
  LEFT JOIN tag ON tag.name = val.name ;

Có lẽ có một số cách khác để làm điều này, có lẽ không cần sử dụng ON CONFLICTcú pháp mới .


4

Không biết làm thế nào điều này sẽ thực hiện nhưng chỉ là một lựa chọn khác để thử, ở đây đang làm theo cách tương tự trường học cũ (không có ON CONFLICT):

WITH items (name) AS (VALUES ('foo'), ('bar'), ('zzz')),
     added        AS
      (
        INSERT INTO tag (name)

        SELECT name FROM items
        EXCEPT
        SELECT name FROM tag

        RETURNING id
      )
SELECT id FROM added

UNION ALL

SELECT id FROM tag
WHERE name IN (SELECT name FROM items)
;

Đó là, chỉ chèn các tên [duy nhất] không tìm thấy trong tagbảng và trả về ID; kết hợp điều đó với ID của các tên tồn tại trong tag, cho đầu ra cuối cùng. Bạn cũng có thể nameđưa vào đầu ra, như được đề xuất bởi ypercubeᵀᴹ , để bạn biết ID nào khớp với tên nào.


1
Vâng. CHỌN cuối cùng của mã của tôi cũng có thể được viết làSELECT .. FROM ins UNION ALL SELECT ... FROM val JOIN tag ... ;
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.