MySQL บนคีย์ที่ซ้ำกัน - รหัสแทรกล่าสุด?


132

ฉันมีคำถามต่อไปนี้:

INSERT INTO table (a) VALUES (0)
  ON DUPLICATE KEY UPDATE a=1

ฉันต้องการรหัสของส่วนแทรกหรือการอัปเดต โดยปกติฉันจะเรียกใช้แบบสอบถามที่สองเพื่อรับสิ่งนี้เนื่องจากฉันเชื่อว่า insert_id () จะส่งคืนเฉพาะ ID ที่ 'แทรก' ไม่ใช่รหัสที่อัปเดต

มีวิธี INSERT / UPDATE และดึง ID ของแถวโดยไม่ต้องเรียกใช้สองแบบสอบถามหรือไม่?


3
แทนที่จะคิดว่าทำไมคุณไม่ทดสอบด้วยตัวเอง? SQL ในการแก้ไขด้านบนใช้งานได้และผ่านการทดสอบของฉันเร็วกว่าการตรวจจับการแทรกล้มเหลวโดยใช้ INSERT IGNORE หรือเลือกเพื่อดูว่ามีการซ้ำกันก่อนหรือไม่
Michael Fenwick

4
คำเตือน: โซลูชันที่เสนอใช้งานได้ แต่ค่า auto_increment ยังคงเพิ่มขึ้นแม้ว่าจะไม่มีการแทรกก็ตาม หากคีย์ที่ซ้ำกันเกิดขึ้นบ่อยครั้งคุณอาจต้องการเรียกใช้alter table tablename AUTO_INCREMENT = 0;หลังจากข้อความค้นหาด้านบนเพื่อหลีกเลี่ยงช่องว่างขนาดใหญ่ในค่า ID ของคุณ
Frank Forte

คำตอบ:


175

ตรวจสอบหน้านี้: https://web.archive.org/web/20150329004325/https://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
ที่ด้านล่างของหน้า พวกเขาอธิบายว่าคุณสามารถทำให้ LAST_INSERT_ID มีความหมายสำหรับการอัปเดตได้อย่างไรโดยส่งนิพจน์ไปยังฟังก์ชัน MySQL นั้น

จากตัวอย่างเอกสาร MySQL:

ถ้าตารางมีคอลัมน์ AUTO_INCREMENT และ INSERT ... UPDATE แทรกแถวฟังก์ชัน LAST_INSERT_ID () จะส่งกลับค่า AUTO_INCREMENT หากคำสั่งอัปเดตแถวแทน LAST_INSERT_ID () จะไม่มีความหมาย อย่างไรก็ตามคุณสามารถแก้ไขปัญหานี้ได้โดยใช้ LAST_INSERT_ID (expr) สมมติว่า id คือคอลัมน์ AUTO_INCREMENT ในการทำให้ LAST_INSERT_ID () มีความหมายสำหรับการอัปเดตให้แทรกแถวดังนี้:

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;

2
ฉันพลาดสิ่งนั้นเมื่อมองไปที่หน้านั้น ดังนั้นส่วนการอัปเดตจะปรากฏเป็น: UPDATE id = LAST_INSERT_ID (id) และใช้งานได้ดี ขอบคุณ!
thekevinscott

7
มันก็บอกว่าฟังก์ชั่น PHP mysql_insert_id () ส่งกลับค่าที่ถูกต้องในทั้งสองกรณี: php.net/manual/en/function.mysql-insert-id.php#59718
jayarjo

2
@PetrPeller - โดยไม่ต้องดูภายใน MySQL อาจหมายความว่ามันจะสร้างค่า แต่ค่านั้นไม่เกี่ยวข้องกับแบบสอบถามที่คุณเพิ่งเรียกใช้ กล่าวอีกนัยหนึ่งคือปัญหาที่สร้างความเจ็บปวดให้กับการแก้ไข
Jason

13
หลังจาก 5.1.12 สิ่งนี้ไม่จำเป็นอีกต่อไปอย่างไรก็ตามฉันพบข้อยกเว้นในวันนี้ หากคุณมี pk การเพิ่มอัตโนมัติและคีย์เฉพาะในการพูดที่อยู่อีเมลและทริกเกอร์ 'เมื่ออัปเดตซ้ำ' ตามที่อยู่อีเมลโปรดทราบว่า last_insert_id 'จะไม่เป็นค่าการเพิ่มอัตโนมัติของแถวที่อัปเดต ดูเหมือนว่าจะเป็นค่าการเพิ่มอัตโนมัติที่แทรกล่าสุด สิ่งนี้สร้างความแตกต่างอย่างมาก การแก้ไขปัญหาจะเหมือนกับที่แสดงไว้ที่นี่กล่าวคือใช้ id = LAST_INSERT_ID (id) ในแบบสอบถามที่อัปเดต
sckd

1
ในความคิดเห็นของ 5.5 @ sckd ยังคงเป็นจริง
e18r

37

เพื่อความถูกต้องหากนี่คือข้อความค้นหาดั้งเดิม:

INSERT INTO table (a) VALUES (0)
 ON DUPLICATE KEY UPDATE a=1

และ 'id' เป็นคีย์หลักที่เพิ่มขึ้นโดยอัตโนมัติซึ่งจะเป็นวิธีการแก้ปัญหา:

INSERT INTO table (a) VALUES (0)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), a=1

ทั้งหมดอยู่ที่นี่: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html

ถ้าตารางมีคอลัมน์ AUTO_INCREMENT และ INSERT ... UPDATE แทรกแถวฟังก์ชัน LAST_INSERT_ID () จะส่งกลับค่า AUTO_INCREMENT หากคำสั่งอัปเดตแถวแทน LAST_INSERT_ID () จะไม่มีความหมาย อย่างไรก็ตามคุณสามารถแก้ไขปัญหานี้ได้โดยใช้ LAST_INSERT_ID (expr) สมมติว่า id คือคอลัมน์ AUTO_INCREMENT


7
ใช่ดูคำตอบที่ยอมรับสำหรับสิ่งที่คุณพูด ไม่ต้องรื้อฟื้นกระทู้ 3 ปี ขอบคุณสำหรับความพยายามของคุณ
fancyPants

1
@tombom เหตุผลเดียวที่ฉันโพสต์คำตอบนี้เพราะคำตอบที่ยอมรับนั้นไม่ถูกต้อง - จะใช้ไม่ได้หากไม่มีอะไรให้อัปเดต
อเล็กซานดาร์โปโปวิช

2

คุณอาจดูที่ REPLACE ซึ่งโดยพื้นฐานแล้วเป็นการลบ / แทรกหากมีระเบียนอยู่ แต่สิ่งนี้จะเปลี่ยนฟิลด์การเพิ่มอัตโนมัติหากมีซึ่งอาจทำลายความสัมพันธ์กับข้อมูลอื่น ๆ


1
อ๊ะใช่ - ฉันกำลังมองหาบางสิ่งที่จะไม่กำจัด ID ก่อนหน้า
thekevinscott

สิ่งนี้อาจเป็นอันตรายเช่นกันเนื่องจากอาจทำให้เกิดการลบข้อมูลอื่นที่เกี่ยวข้อง (ตามข้อ จำกัด )
เสิร์จ


1

ฉันพบปัญหาเมื่อ ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID (id) เพิ่มคีย์หลักทีละ 1 ดังนั้น id ของอินพุตถัดไปภายในเซสชันจะเพิ่มขึ้นทีละ 2


0

เป็นที่น่าสังเกตและสิ่งนี้อาจชัดเจน (แต่ฉันจะพูดต่อไปเพื่อความชัดเจนที่นี่) REPLACE จะทำลายแถวการจับคู่ที่มีอยู่ก่อนที่จะแทรกข้อมูลใหม่ของคุณ ON DUPLICATE KEY UPDATE จะอัปเดตเฉพาะคอลัมน์ที่คุณระบุและรักษาแถวไว้

จากคู่มือ :

REPLACE ทำงานเหมือนกับ INSERT ทุกประการยกเว้นว่าถ้าแถวเก่าในตารางมีค่าเดียวกันกับแถวใหม่สำหรับ PRIMARY KEY หรือดัชนี UNIQUE แถวเก่าจะถูกลบออกก่อนที่จะแทรกแถวใหม่


0

โซลูชันที่มีอยู่จะใช้ได้ผลหากคุณใช้การเพิ่มอัตโนมัติ ฉันมีสถานการณ์ที่ผู้ใช้สามารถกำหนดคำนำหน้าได้และควรรีสตาร์ทลำดับที่ 3000 เนื่องจากคำนำหน้าที่แตกต่างกันนี้ฉันไม่สามารถใช้การเพิ่มอัตโนมัติซึ่งทำให้ last_insert_id ว่างเปล่าสำหรับส่วนแทรก ฉันแก้ไขด้วยสิ่งต่อไปนี้:

INSERT INTO seq_table (prefix, id) VALUES ('$user_prefix', LAST_INSERT_ID(3000)) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id + 1);
SELECT LAST_INSERT_ID();

หากมีคำนำหน้าอยู่คำนำหน้าจะเพิ่มขึ้นและเติมข้อมูล last_insert_id หากไม่มีคำนำหน้าจะแทรกคำนำหน้าด้วยค่า 3000 และเติมข้อมูล last_insert_id ด้วย 3000

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.