วิธีรับ ID ของแถวที่ขัดแย้งกันใน upsert?


18

ฉันมีตารางที่tagมี 2 ​​คอลัมน์: id(uuid) และname(ข้อความ) ตอนนี้ฉันต้องการแทรกแท็กใหม่ลงในตาราง แต่ถ้ามีแท็กนั้นอยู่แล้วฉันต้องการเพียงรับidบันทึกที่มีอยู่

ฉันคิดว่าฉันสามารถใช้ON CONFLICT DO NOTHINGร่วมกับRETURNING "id":

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

แต่จะส่งคืนชุดผลลัพธ์ที่ว่างเปล่าหากมีแท็กชื่อ "foo" อยู่แล้ว

ฉันเปลี่ยนแบบสอบถามเพื่อใช้ส่วนDO UPDATEคำสั่งnoop :

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

มันใช้งานได้ตามที่ตั้งใจ แต่มันค่อนข้างสับสนเพราะฉันเพิ่งตั้งชื่อให้เป็นค่าที่มีอยู่แล้ว

นี่เป็นวิธีที่จะแก้ไขปัญหานี้หรือมีวิธีที่ง่ายกว่าที่ฉันคิดถึง


คุณลองreturning excluded.idไหม
a_horse_with_no_name

@a_horse_with_no_name ที่เพิ่งให้ฉันเมื่อมีการใช้ERROR: missing FROM-clause entry for table "excluded" DO NOTHING
Der Hochstapler

1
ดูเพิ่มเติมที่stackoverflow.com/a/42217872/1411457
อันตราย

คำตอบ:


8

สิ่งนี้จะใช้งานได้ (เท่าที่ฉันทดสอบ) ในทั้ง 3 กรณีหากค่าที่จะแทรกเป็นค่าใหม่หรือทั้งหมดในตารางหรือมิกซ์:

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 ;

อาจมีวิธีอื่นในการทำเช่นนี้โดยอาจไม่ได้ใช้ON CONFLICTไวยากรณ์ใหม่


4

ไม่มีความคิดว่ามันจะทำงานได้อย่างไร แต่ก็เป็นอีกทางเลือกในการลองนี่คือทำแบบเดียวกับที่โรงเรียนเก่า (โดยไม่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)
;

นั่นคือแทรกเฉพาะชื่อ [ที่ไม่ซ้ำกัน] ที่ไม่พบในtagตารางและส่งคืน ID; รวมกับ IDs ของชื่อที่มีอยู่ในtagสำหรับผลลัพธ์สุดท้าย นอกจากนี้คุณยังสามารถnameส่งออกตามที่แนะนำโดยypercubeᵀᴹเพื่อให้คุณรู้ว่า ID ใดตรงกับชื่อใด


1
ใช่. เลือกสุดท้ายของรหัสของฉันยังสามารถเขียนเป็นSELECT .. FROM ins UNION ALL SELECT ... FROM val JOIN tag ... ;
ypercubeᵀᴹ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.