ตารางตัวอย่างและข้อมูล
CREATE TABLE dupes(col1 int primary key, col2 int, col3 text,
CONSTRAINT col2_unique UNIQUE (col2)
);
INSERT INTO dupes values(1,1,'a'),(2,2,'b');
สร้างปัญหาซ้ำ
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1) DO UPDATE SET col3 = 'c', col2 = 2
เรียกสิ่งนี้ว่า Q1 ผลลัพธ์คือ
ERROR: duplicate key value violates unique constraint "col2_unique"
DETAIL: Key (col2)=(2) already exists.
สิ่งที่เอกสารกล่าวว่า
ขัดแย้ง _target สามารถทำการอนุมานดัชนีที่ไม่ซ้ำกัน เมื่อทำการอนุมานประกอบด้วยคอลัมน์ index_column_name อย่างน้อยหนึ่งคอลัมน์และ / หรือนิพจน์ index_expression และ index_predicate ซึ่งเป็นทางเลือก ดัชนีที่ไม่ซ้ำกันของ table_name ทั้งหมดที่โดยไม่คำนึงถึงลำดับมีคอลัมน์ / นิพจน์ที่ระบุข้อขัดแย้งทั้งหมดถูกอนุมาน (ถูกเลือก) เป็นดัชนีชี้ขาด หากมีการระบุ index_predicate จะต้องเป็นไปตามข้อกำหนดเพิ่มเติมสำหรับการอนุมานเพื่อให้เป็นไปตามดัชนีอนุญาโตตุลาการ
สิ่งนี้ทำให้รู้สึกว่าแบบสอบถามต่อไปนี้ควรใช้งานได้ แต่ไม่ได้เป็นเพราะจริงๆแล้วจะต้องใช้ดัชนีเฉพาะร่วมกันบน col1 และ col2 อย่างไรก็ตามดัชนีดังกล่าวไม่สามารถรับประกันได้ว่า col1 และ col2 จะไม่ซ้ำกันซึ่งเป็นหนึ่งในข้อกำหนดของ OP
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1,col2) DO UPDATE SET col3 = 'c', col2 = 2
เรียกแบบสอบถามนี้ว่า Q2 (สิ่งนี้ล้มเหลวด้วยข้อผิดพลาดทางไวยากรณ์)
ทำไม?
Postgresql ทำงานในลักษณะนี้เป็นเพราะสิ่งที่ควรเกิดขึ้นเมื่อความขัดแย้งเกิดขึ้นในคอลัมน์ที่สองไม่ได้กำหนดไว้อย่างดี มีจำนวนของความเป็นไปได้ ตัวอย่างเช่นในแบบสอบถาม Q1 ข้างต้นควรอัพเดต postgresql col1
เมื่อมีข้อขัดแย้งบนcol2
? แต่ถ้านั่นนำไปสู่ความขัดแย้งอีกcol1
ล่ะ? postgresql คาดว่าจะจัดการอย่างไร
วิธีแก้ปัญหา
วิธีการแก้ปัญหาคือการรวมเกี่ยวกับความขัดแย้งกับUpsert สมัยเก่า
CREATE OR REPLACE FUNCTION merge_db(key1 INT, key2 INT, data TEXT) RETURNS VOID AS
$$
BEGIN
LOOP
UPDATE dupes SET col3 = data WHERE col1 = key1 and col2 = key2;
IF found THEN
RETURN;
END IF;
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col1) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col2) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
END;
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
คุณจะต้องแก้ไขตรรกะของฟังก์ชันที่จัดเก็บไว้นี้เพื่อให้อัปเดตคอลัมน์ตามที่คุณต้องการ วิงวอนมันเช่น
SELECT merge_db(3,2,'c');
SELECT merge_db(1,2,'d');