9.5 และใหม่กว่า:
PostgreSQL 9.5 และการสนับสนุนที่ใหม่กว่าINSERT ... ON CONFLICT UPDATE
(และON CONFLICT DO NOTHING
) เช่นสุดยอด
ON DUPLICATE KEY UPDATE
เมื่อเทียบกับ
คำอธิบายด่วนคำอธิบายอย่างรวดเร็ว
สำหรับการใช้งานดูคู่มือ - เฉพาะconflict_actionประโยคในแผนภาพไวยากรณ์และข้อความอธิบาย
ซึ่งแตกต่างจากโซลูชั่นสำหรับ 9.4 และรุ่นเก่ากว่าที่ได้รับด้านล่างคุณสมบัตินี้ทำงานร่วมกับหลายแถวที่ขัดแย้งกันและไม่จำเป็นต้องล็อคแบบเอกสิทธิ์เฉพาะบุคคลหรือวนซ้ำ
กระทำการเพิ่มคุณลักษณะอยู่ที่นี่และอภิปรายรอบการพัฒนาที่อยู่ที่นี่
หากคุณอายุ 9.5 ปีและไม่จำเป็นต้องรองรับการย้อนกลับคุณสามารถหยุดอ่านได้ทันที
9.4 และมากกว่า:
PostgreSQL ไม่มีสิ่งอำนวยความสะดวกในตัวUPSERT
(หรือMERGE
) และการทำอย่างมีประสิทธิภาพเมื่อเผชิญกับการใช้งานพร้อมกันนั้นเป็นเรื่องยากมาก
บทความนี้กล่าวถึงปัญหาในรายละเอียดที่เป็นประโยชน์บทความนี้กล่าวถึงปัญหาในรายละเอียดที่เป็นประโยชน์
โดยทั่วไปคุณต้องเลือกระหว่างสองตัวเลือก:
- การดำเนินการแทรก / อัพเดตส่วนบุคคลในการลองส่งซ้ำ หรือ
- ล็อคตารางและทำการผสานแบทช์
แต่ละแถวลองซ้ำแถว
การใช้แต่ละแถว upserts ในการวนซ้ำเป็นตัวเลือกที่เหมาะสมถ้าคุณต้องการการเชื่อมต่อจำนวนมากพร้อมกันพยายามที่จะทำการแทรก
เอกสาร PostgreSQL มีขั้นตอนที่มีประโยชน์ที่จะช่วยให้คุณสามารถทำเช่นนี้ในวงภายในฐานข้อมูล มันป้องกันการอัพเดทที่สูญหายและแทรกการแข่งขัน มันจะทำงานในREAD COMMITTED
โหมดและจะปลอดภัยก็ต่อเมื่อมันเป็นสิ่งเดียวที่คุณทำในการทำธุรกรรม ฟังก์ชั่นจะทำงานไม่ถูกต้องหากทริกเกอร์หรือคีย์ที่ไม่ซ้ำรองทำให้เกิดการละเมิดที่ไม่ซ้ำกัน
กลยุทธ์นี้ไม่มีประสิทธิภาพมาก เมื่อใดก็ตามที่ปฏิบัติได้จริงคุณควรเข้าคิวและทำงานเป็นกลุ่มให้มากขึ้นดังที่อธิบายไว้ด้านล่างแทน
วิธีแก้ไขปัญหาที่พยายามทำหลายอย่างล้มเหลวในการพิจารณาการย้อนกลับดังนั้นจึงส่งผลให้มีการปรับปรุงที่ไม่สมบูรณ์ ธุรกรรมสองรายการแข่งขันกันเอง หนึ่งในนั้นประสบความสำเร็จINSERT
s; อีกอันหนึ่งได้รับข้อผิดพลาดของคีย์ที่ซ้ำกันและทำUPDATE
แทน UPDATE
บล็อกรอINSERT
ที่จะย้อนกลับหรือกระทำ เมื่อย้อนกลับUPDATE
เงื่อนไขการตรวจสอบอีกครั้งจะจับคู่กับศูนย์แถวดังนั้นแม้ว่าการUPDATE
กระทำดังกล่าวจะไม่ได้เพิ่มยอดขายตามที่คุณคาดหวัง คุณต้องตรวจสอบจำนวนแถวผลลัพธ์และลองอีกครั้งตามที่จำเป็น
โซลูชันที่พยายามทำบางอย่างยังไม่สามารถพิจารณาเชื้อชาติที่เลือกได้ หากคุณลองชัดเจนและเรียบง่าย:
-- 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;
จากนั้นเมื่อสองรันพร้อมกันมีโหมดความล้มเหลวหลายโหมด หนึ่งคือปัญหาที่กล่าวถึงแล้วด้วยการตรวจสอบการปรับปรุงอีกครั้ง อีกประการหนึ่งคือที่ทั้งสองUPDATE
ในเวลาเดียวกันการจับคู่เป็นศูนย์แถวและดำเนินการต่อ จากนั้นพวกเขาทั้งสองทำEXISTS
ทดสอบที่เกิดขึ้นก่อนที่จะ INSERT
ทั้งคู่รับแถวศูนย์ดังนั้นทั้งคู่จึงทำINSERT
ทั้งสองได้รับการศูนย์แถวเพื่อให้ทั้งสองทำหนึ่งล้มเหลวด้วยข้อผิดพลาดของคีย์ที่ซ้ำกัน
นี่คือเหตุผลที่คุณต้องลองวนซ้ำ คุณอาจคิดว่าคุณสามารถป้องกันข้อผิดพลาดที่สำคัญที่ซ้ำกันหรือการปรับปรุงที่สูญหายด้วย SQL ที่ฉลาด แต่คุณทำไม่ได้ คุณต้องตรวจสอบจำนวนแถวหรือจัดการข้อผิดพลาดที่สำคัญซ้ำ (ขึ้นอยู่กับวิธีที่เลือก) และลองอีกครั้ง
โปรดอย่าม้วนโซลูชันของคุณเองสำหรับสิ่งนี้ เช่นเดียวกับการรอคิวข้อความอาจผิดปกติ
จำนวนมากขึ้นพร้อมล็อค
บางครั้งคุณต้องการเพิ่มจำนวนมากขึ้นซึ่งคุณมีชุดข้อมูลใหม่ที่คุณต้องการผสานเข้ากับชุดข้อมูลที่มีอยู่เดิม สิ่งนี้มีประสิทธิภาพมากกว่าการตั้งแถวเดี่ยวอย่างมากและควรได้รับการพิจารณาเมื่อใช้งานได้จริง
ในกรณีนี้คุณมักจะทำตามขั้นตอนต่อไปนี้:
CREATE
TEMPORARY
ตาราง
COPY
หรือแทรกข้อมูลใหม่จำนวนมากลงในตารางชั่วคราว
LOCK
IN EXCLUSIVE MODE
ตารางเป้าหมาย สิ่งนี้อนุญาตให้ทำธุรกรรมอื่น ๆSELECT
แต่ไม่ทำการเปลี่ยนแปลงใด ๆ ในตาราง
ทำUPDATE ... FROM
ระเบียนที่มีอยู่โดยใช้ค่าในตาราง temp
ทำINSERT
แถวที่ไม่มีอยู่ในตารางเป้าหมาย
COMMIT
ปลดล็อค
ตัวอย่างเช่นสำหรับตัวอย่างที่ให้ไว้ในคำถามการใช้หลายค่าINSERT
เพื่อเติมข้อมูลตารางชั่วคราว:
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;
การอ่านที่เกี่ยวข้อง
เกี่ยวกับMERGE
อะไร
MERGE
จริง ๆ แล้วมาตรฐาน SQL มีซีแมนทิกส์กำหนดไว้ไม่ดีและไม่เหมาะสำหรับการ upserting โดยไม่ต้องล็อคตารางก่อน
มันเป็นคำสั่ง OLAP ที่มีประโยชน์จริง ๆ สำหรับการรวมข้อมูล แต่จริงๆแล้วไม่ใช่โซลูชันที่มีประโยชน์สำหรับการใช้งานพร้อมกันที่ปลอดภัย มีคำแนะนำมากมายสำหรับผู้ที่ใช้ DBMS อื่น ๆ เพื่อใช้MERGE
สำหรับ upserts แต่จริงๆแล้วมันผิด
ฐานข้อมูลอื่น ๆ :