Чи гарантована правильність функції MySql LAST_INSERT_ID ()?


36

Коли я роблю один рядок INSERTдо таблиці, що містить AUTO_INCREMENTстовпець, я хотів би використовувати LAST_INSERT_ID()функцію для повернення нового AUTO_INCREMENT'ed значення, збереженого для цього рядка.

Оскільки багато розробників та адміністраторів Microsoft SQL Server, без сумніву, усвідомлюють, що еквівалентна функціональність у SQL Server ( SCOPE_IDENTITYі @@IDENTITY) не була без проблем .

Я знаю стан документів MySQL:

Створений ідентифікатор зберігається на сервері на основі кожного з'єднання . Це означає, що значення, повернене функцією даному клієнту, є першим AUTO_INCREMENTзначенням, сформованим для останнього оператора, що впливає на AUTO_INCREMENTстовпець цього клієнта . На це значення не можуть впливати інші клієнти, навіть якщо вони генерують AUTO_INCREMENTвласні значення. Така поведінка гарантує, що кожен клієнт може отримати власний ідентифікатор, не турбуючись про діяльність інших клієнтів та без необхідності блокування чи транзакцій.

(джерело)

і навіть зайти так далеко, щоб сказати:

Використання 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 є частиною складеного первинного ключа і не є першим стовпцем у ключі.


якщо у вас "ON DUPLICATE KEY UPDATE", який також повертає 0, якщо нічого не оновлюється, якщо ви встановите деякий date_field = now (), він завжди повернеться правильно
max4ever

7

Щоб додатково розширити точку № 2 у відповіді, наданій DTest:

У версіях MySQL , які я використовував, це гарна ідея , щоб в явній формі скинути значення 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 і отримуєте ідентифікатор 5, потім вставляєте в TB (але він не вдається), але ви все ще бачите 5. На підставі цього ви думаєте, що ви тільки що створили новий рядок в TA з id 5 та новий рядок у TB з id 5, тоді як насправді в рядку TB з id 5 немає, або такий рядок існує, але він насправді не має нічого спільного з будь-яким кодом, який ви просто побіг.


2
По-перше, ви не повинні були використовувати last_insert_id()судження, щоб запит був успішним. Адже це останній вставлений ідентифікатор, який містить потрібні вам значення, коли ви вже знали, що досягли успіху.
Pacerier
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.