Apakah fungsi LAST_INSERT_ID () MySql dijamin benar?

36

Ketika saya melakukan satu baris INSERTke tabel yang memiliki AUTO_INCREMENTkolom saya ingin menggunakan LAST_INSERT_ID()fungsi untuk mengembalikan nilai baru yang AUTO_INCREMENTdisimpan untuk baris itu.

Karena banyak Microsoft SQL Server devs dan admin tidak diragukan lagi menyadari fungsi yang setara dalam SQL Server ( SCOPE_IDENTITYdan @@IDENTITY) belum tanpa masalah .

Saya tahu status dokumen MySQL:

ID yang dihasilkan dipertahankan di server berdasarkan per koneksi . Ini berarti bahwa nilai yang dikembalikan oleh fungsi ke klien yang diberikan adalah nilai pertama yang AUTO_INCREMENTdihasilkan untuk pernyataan terbaru yang memengaruhi AUTO_INCREMENTkolom oleh klien itu . Nilai ini tidak dapat dipengaruhi oleh klien lain, bahkan jika mereka menghasilkan AUTO_INCREMENTnilai sendiri. Perilaku ini memastikan bahwa setiap klien dapat mengambil ID sendiri tanpa memperhatikan aktivitas klien lain, dan tanpa perlu kunci atau transaksi.

(sumber)

dan bahkan melangkah lebih jauh dengan mengatakan:

Penggunaan LAST_INSERT_ID()dan AUTO_INCREMENTkolom secara simultan dari banyak klien benar-benar valid.

(sumber)

Adakah risiko atau skenario yang diketahui yang dapat menyebabkan LAST_INSERT_ID()tidak mengembalikan nilai yang benar?

Saya menggunakan MySQL 5.5 pada CentOS 5.5 x64 dan Fedora 16 x64 dan mesin InnoDB.

Kev
sumber

Jawaban:

35

Beberapa peringatan yang ingin saya tunjukkan saat menggunakan LAST_INSERT_ID:

  1. Saya tahu Anda menyebutkan sisipan satu baris. Tetapi ketika melakukan multiple-row insert, LAST_INSERT_ID()akan mengembalikan nilai dari baris pertama yang dimasukkan (bukan yang terakhir).

  2. Jika penyisipan gagal, LAST_INSERT_ID()akan ditentukan. Hal yang sama berlaku untuk kemunduran transaksi secara otomatis (karena kesalahan).

  3. Jika Anda memasukkan dalam transaksi yang berhasil, dan Anda masih mengeluarkan a ROLLBACK, LAST_INSERT_ID()akan dibiarkan seperti sebelum rollback.

  4. Ada beberapa peringatan ketika menggunakan AUTO_INCREMENTdan LAST_INSERT_IDdalam replikasi berbasis pernyataan. Makhluk pertama saat digunakan dalam pemicu atau fungsi. Skenario kedua adalah skenario yang kurang umum di mana kolom auto_increment Anda adalah bagian dari kunci primer komposit dan bukan kolom pertama dalam kunci tersebut.

Derek Downey
sumber
jika Anda memiliki "ON DUPLICATE KEY UPDATE" yang juga mengembalikan 0 jika tidak ada yang diperbarui, jika Anda menetapkan beberapa date_field = sekarang () itu akan selalu kembali dengan benar
max4ever
7

Untuk memperluas lebih lanjut pada poin nomor 2 dalam jawaban yang diberikan oleh DTest:

Pada versi MySQL yang telah saya gunakan, adalah ide yang bagus untuk secara eksplisit mengatur ulang nilai LAST_INSERT_ID sebelum setiap blok kode tempat Anda berencana melakukan penyisipan.

Ini bisa dilakukan seperti ini:

-- 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();

Setelah serangkaian pernyataan di atas dijalankan, Anda akan tahu apakah sisipan memiliki pengaruh dengan memeriksa apakah LAST_INSERT_ID masih disetel ke "some_flag_init_value_of_your_choice" di akhir eksekusi.

Jika tidak, Anda dapat berakhir dengan situasi bermasalah berikut:

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'.

Karena sisipan kedua telah gagal , Anda mungkin mengharapkan bahwa panggilan kedua ke LAST_INSERT_ID akan mengembalikan NULL atau bahwa ia akan menghasilkan set hasil kosong (nol baris). Fakta bahwa itu masih mengembalikan pengenal bilangan bulat yang valid dapat menyesatkan Anda untuk berpikir bahwa sisipan kedua SUCCEEDED ketika itu tidak berhasil.

Segala sesuatunya menjadi BAHKAN WEIRDER ketika Anda menganggap bahwa LAST_INSERT_ID akan terus melestarikan dan mengulang id unik yang berhasil terakhir bahkan jika pernyataan insert gagal berikutnya menargetkan tabel yang berbeda dari tabel yang menghasilkan id unik sukses terakhir. Dengan kata lain, Anda memasukkan TA tabel dan mendapatkan id 5, lalu memasukkan TB (tetapi gagal), tetapi Anda masih melihat 5. Berdasarkan itu, Anda berpikir bahwa Anda baru saja membuat baris baru dalam TA dengan id 5 dan baris baru dalam TB dengan id 5, sedangkan pada kenyataannya tidak ada baris dalam TB dengan id 5, atau memang ada baris seperti itu tetapi sebenarnya tidak ada hubungannya dengan kode apa pun yang Anda baru saja berlari.

pestophagous
sumber
2
Di tempat pertama Anda seharusnya tidak menggunakan keberadaan last_insert_id()untuk menilai jika permintaan telah berhasil. Lagipula, ini adalah Id yang dimasukkan terakhir, menyimpan nilai yang Anda butuhkan ketika Anda sudah tahu bahwa Anda telah berhasil.
Pacerier