สไตล์ถามตอบ
หลังจากค้นคว้าและต่อสู้กับปัญหาเป็นเวลาหลายชั่วโมงฉันพบว่ามีสองวิธีในการทำสิ่งนี้ขึ้นอยู่กับโครงสร้างของโต๊ะของคุณและหากคุณเปิดใช้งานข้อ จำกัด ของคีย์ต่างประเทศเพื่อรักษาความสมบูรณ์ ฉันต้องการแบ่งปันสิ่งนี้ในรูปแบบที่สะอาดเพื่อประหยัดเวลาให้กับผู้คนที่อาจอยู่ในสถานการณ์ของฉัน
ตัวเลือกที่ 1: คุณสามารถลบแถวได้
กล่าวอีกนัยหนึ่งคุณไม่มีรหัสภายนอกหรือหากคุณมีอยู่โปรแกรม SQLite ของคุณจะได้รับการกำหนดค่าเพื่อให้ไม่มีข้อยกเว้นด้านความสมบูรณ์ วิธีที่จะไปเป็นแทรกหรือแทนที่ หากคุณกำลังพยายามแทรก / อัปเดตผู้เล่นที่มี ID อยู่แล้วเอ็นจิ้น SQLite จะลบแถวนั้นและแทรกข้อมูลที่คุณให้ คำถามมาแล้ว: จะทำอย่างไรเพื่อให้ ID เก่าเชื่อมโยง?
สมมติว่าเราต้องการUPSERTด้วย data user_name = 'steven' และ age = 32
ดูรหัสนี้:
INSERT INTO players (id, name, age)
VALUES (
coalesce((select id from players where user_name='steven'),
(select max(id) from drawings) + 1),
32)
เคล็ดลับอยู่ที่การรวมตัวกัน ส่งคืน id ของผู้ใช้ 'steven' ถ้ามีมิฉะนั้นจะส่งคืน id ใหม่
ตัวเลือกที่ 2: คุณไม่สามารถลบแถวได้
หลังจากลองใช้วิธีแก้ปัญหาก่อนหน้านี้แล้วฉันก็รู้ว่าในกรณีของฉันที่สามารถทำลายข้อมูลได้เนื่องจาก ID นี้ทำงานเป็นคีย์ต่างประเทศสำหรับตารางอื่น นอกจากนี้ฉันสร้างตารางด้วยประโยคON DELETE CASCADEซึ่งหมายความว่าจะลบข้อมูลอย่างเงียบ ๆ อันตราย.
ดังนั้นครั้งแรกที่ผมคิดว่าประโยค IF แต่ SQLite มีเพียงกรณี และไม่สามารถใช้CASEนี้ได้ (หรืออย่างน้อยฉันก็ไม่ได้จัดการ) เพื่อทำการค้นหาUPDATEหนึ่งรายการหาก EXISTS (เลือก id จากผู้เล่นที่ user_name = 'steven') และINSERTหากไม่เป็นเช่นนั้น ไม่ไป.
แล้วในที่สุดฉันก็ใช้กำลังเดรัจฉานและประสบความสำเร็จ ตรรกะคือสำหรับUPSERTแต่ละเครื่องที่คุณต้องการดำเนินการขั้นแรกให้เรียกใช้INSERT หรือ IGNOREเพื่อให้แน่ใจว่ามีแถวกับผู้ใช้ของเราจากนั้นดำเนินการค้นหาUPDATEด้วยข้อมูลเดียวกับที่คุณพยายามแทรก
ข้อมูลเหมือนเดิม: user_name = 'steven' และ age = 32
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);
UPDATE players SET user_name='steven', age=32 WHERE user_name='steven';
และนั่นคือทั้งหมด!
แก้ไข
ตามที่แอนดี้ได้แสดงความคิดเห็นการพยายามแทรกก่อนแล้วอัปเดตอาจทำให้ทริกเกอร์เริ่มทำงานบ่อยกว่าที่คาดไว้ นี่ไม่ได้อยู่ในความคิดของฉันว่าเป็นปัญหาด้านความปลอดภัยของข้อมูล แต่เป็นความจริงที่ว่าการยิงเหตุการณ์ที่ไม่จำเป็นนั้นไม่สมเหตุสมผล ดังนั้นวิธีแก้ไขที่ดีขึ้นคือ:
UPDATE players SET age=32 WHERE user_name='steven';
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);