ฟังก์ชัน LAST_INSERT_ID () ของ MySql รับประกันว่าถูกต้องหรือไม่


36

เมื่อฉันทำแถวเดียวINSERTไปยังตารางที่มีAUTO_INCREMENTคอลัมน์ฉันต้องการใช้LAST_INSERT_ID()ฟังก์ชันเพื่อส่งกลับAUTO_INCREMENTค่า ed ' ใหม่ที่เก็บไว้สำหรับแถวนั้น

Microsoft SQL Server และผู้ดูแลระบบหลายคนนั้นไม่ต้องสงสัยเลยว่าทราบถึงหน้าที่การใช้งานที่เทียบเท่าใน SQL Server ( SCOPE_IDENTITYและ@@IDENTITY) ที่ไม่เคยมีปัญหามาก่อน

ฉันรู้สถานะของเอกสาร MySQL:

ID ที่ถูกสร้างขึ้นจะถูกเก็บไว้ในเซิร์ฟเวอร์ในแต่ละการเชื่อมต่อพื้นฐาน ซึ่งหมายความว่าค่าที่ส่งคืนโดยฟังก์ชันไปยังไคลเอนต์ที่กำหนดเป็นAUTO_INCREMENTค่าแรกที่สร้างขึ้นสำหรับคำสั่งล่าสุดที่มีผลต่อAUTO_INCREMENTคอลัมน์โดยไคลเอ็นต์นั้น ค่านี้ไม่สามารถรับผลกระทบจากไคลเอนต์อื่นแม้ว่าพวกเขาสร้างAUTO_INCREMENTค่าของตนเอง พฤติกรรมนี้ทำให้มั่นใจได้ว่าลูกค้าแต่ละคนสามารถดึงข้อมูล ID ของตนเองได้โดยไม่ต้องกังวลกับกิจกรรมของลูกค้ารายอื่นและไม่จำเป็นต้องล็อคหรือทำธุรกรรม

(ที่มา)

และแม้กระทั่งไปไกลถึงจะบอกว่า:

การใช้LAST_INSERT_ID()และAUTO_INCREMENTคอลัมน์พร้อมกันจากไคลเอนต์หลายคนถูกต้องสมบูรณ์

(ที่มา)

มีความเสี่ยงหรือสถานการณ์ที่ทราบที่อาจทำให้LAST_INSERT_ID()ไม่คืนค่าที่ถูกต้องหรือไม่?

ฉันใช้ MySQL 5.5 บน CentOS 5.5 x64 และ Fedora 16 x64 และเอ็นจิ้น InnoDB

คำตอบ:


35

คำเตือนสองสามข้อที่ฉันต้องการชี้ให้เห็นเมื่อใช้LAST_INSERT_ID:

  1. ฉันรู้ว่าคุณพูดถึงส่วนแทรกแบบแถวเดียว แต่เมื่อทำการแทรกหลายแถวLAST_INSERT_ID()จะคืนค่าของแถวแรกที่แทรก (ไม่ใช่อันสุดท้าย)

  2. หากการแทรกล้มเหลวLAST_INSERT_ID()จะไม่ถูกกำหนด เช่นเดียวกับการย้อนกลับธุรกรรมโดยอัตโนมัติ (เนื่องจากข้อผิดพลาด)

  3. ถ้าคุณทำใส่ในการทำธุรกรรมที่ประสบความสำเร็จและคุณยังคงออกROLLBACK, LAST_INSERT_ID()จะถูกทิ้งเพราะมันเป็นอะไรก่อนที่จะย้อนกลับ

  4. มีข้อแม้คู่เมื่อใช้AUTO_INCREMENTและLAST_INSERT_IDในการจำลองแบบตามคำสั่ง สิ่งมีชีวิตแรกเมื่อใช้ในทริกเกอร์หรือฟังก์ชัน เหตุการณ์ที่สองเป็นสถานการณ์ที่พบได้ทั่วไปน้อยลงโดยที่คอลัมน์ auto_increment ของคุณเป็นส่วนหนึ่งของคีย์หลักผสมและไม่ใช่คอลัมน์แรกในคีย์


7

หากต้องการขยายเพิ่มเติมในจุดที่ 2 ในคำตอบของ DTest:

ในรุ่นของ MySQL ที่ฉันได้ใช้มันเป็นความคิดที่ดีที่จะexplicityรีเซ็ตค่าของ LAST_INSERT_ID ก่อนที่จะบล็อกของแต่ละรหัสที่คุณวางแผนที่จะดำเนินการแทรก

สามารถทำได้ดังนี้:

-- initialize the LAST_INSERT_ID to some flag value:
SELECT LAST_INSERT_ID( some_flag_init_value_of_your_choice );
-- perform the insert  
INSERT INTO ttt (ccc) VALUES (vvv);
-- retrieve the id of the inserted row:  
SELECT LAST_INSERT_ID();

หลังจากดำเนินการชุดคำสั่งด้านบนแล้วคุณจะรู้ว่าส่วนแทรกมีผลกระทบใด ๆ หรือไม่โดยตรวจสอบว่า LAST_INSERT_ID ยังคงถูกตั้งค่าเป็น "some_flag_init_value_of_your_choice" เมื่อสิ้นสุดการดำเนินการ

มิฉะนั้นคุณสามารถปิดท้ายด้วยสถานการณ์ที่มีปัญหาต่อไปนี้:

INSERT INTO ttt ( ccc ) VALUES ( 'a' );    -- assume this succeeds.
SELECT LAST_INSERT_ID();                   -- this will return the unique id of the new row with value 'a'.
INSERT INTO ttt ( ccc ) VALUES ( 'b' );    -- assume this FAILS.
SELECT LAST_INSERT_ID();                   -- this will STILL RETURN the unique id of the row with 'a'.

เนื่องจากการแทรกครั้งที่สองล้มเหลวคุณอาจได้รับการคาดหวังว่าการเรียกครั้งที่สองไปที่ LAST_INSERT_ID อาจส่งคืนค่า NULL หรือว่าจะสร้างชุดผลลัพธ์ที่ว่างเปล่า (แถวศูนย์) ความจริงที่ว่ามันยังคงส่งคืนตัวระบุจำนวนเต็มที่ถูกต้องอาจทำให้คุณเข้าใจผิดคิดว่าการแทรกครั้งที่สองสำเร็จเมื่อไม่ได้

สิ่งที่ได้รับแม้กระทั่งเมื่อคุณพิจารณาว่า LAST_INSERT_ID จะยังคงรักษาและทำซ้ำรหัสเฉพาะที่ประสบความสำเร็จล่าสุดแม้ว่าคำสั่งแทรกที่ล้มเหลวในภายหลังจะมีการกำหนดเป้าหมายตารางที่แตกต่างจากตารางที่สร้างรหัสเฉพาะที่ประสบความสำเร็จล่าสุด กล่าวอีกนัยหนึ่งคุณแทรกลงในตาราง TA และรับ id 5 จากนั้นคุณแทรกลงใน TB (แต่ล้มเหลว) แต่คุณยังเห็น 5 ตามนั้นคุณคิดว่าคุณเพิ่งสร้างแถวใหม่ใน TA ด้วย id 5 และแถวใหม่ใน TB ที่มี id 5 ในขณะที่ในความเป็นจริงไม่มีแถวใด ๆ ใน TB ที่มี id 5 หรือมีแถวดังกล่าวอยู่ แต่จริงๆแล้วมันไม่มีอะไรเกี่ยวข้องกับรหัสที่คุณเพิ่งทำ วิ่ง


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