MySQL ON DUPLICATE KEY - ідентифікатор останньої вставки?


132

У мене є такий запит:

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

Я хочу ідентифікатор або вставки, або оновлення. Зазвичай я запускаю другий запит, щоб отримати це, оскільки я вважаю, що insert_id () повертає лише "вставлений" ідентифікатор, а не оновлений ідентифікатор.

Чи є спосіб ВСТАВКИ / ОНОВЛЕННЯ та отримання ідентифікатора рядка без запуску двох запитів?


3
Замість того, щоб припустити, чому б ви не випробували його самостійно? SQL у редакції вище працює, і моє тестування проходить швидше, ніж виявити помилку вставки, використовуючи INSERT IGNORE або вибравши, чи спочатку є дублікат.
Майкл Фенвік

4
УВАГА: Пропоноване рішення працює, але значення auto_increment продовжує збільшуватися, навіть якщо немає вставки. Якщо повторюваний ключ трапляється часто, ви можете запустити alter table tablename AUTO_INCREMENT = 0;після вищезазначеного запиту, щоб уникнути великих прогалин у ваших значеннях id.
Френк Форте

Відповіді:


175

Перевірте цю сторінку: https://web.archive.org/web/20150329004325/https://dev.mysql.com/doc/refman/5.0/uk/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, це, ймовірно, означає, що воно виробить значення, але це значення не пов'язане з запитом, який ви тільки що виконували. Іншими словами, проблема, яка є налагодженням болю.
Джейсон

13
Після 5.1.12 це, мабуть, більше не потрібно, проте сьогодні я знайшов виняток із цього. Якщо у вас є автоматичне підвищення ПК, унікальний ключ на адресу електронної пошти, а також тригери "на повторне оновлення дублікатів" на основі адреси електронної пошти, зауважте, що 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
Так, дивіться прийняту відповідь на те саме, що ви сказали. Не потрібно відроджувати трирічні посади. Дякуємо за ваші зусилля.
fancyPants

1
@tombom Єдина причина, чому я опублікував цю відповідь, це тому, що прийнята відповідь є невірною - вона не працюватиме, якщо немає чого оновити.
Олександр Попович

2

Ви можете подивитися REPLACE, який по суті є видаленням / вставкою, якщо запис існує. Але це змінило б поле автоматичного збільшення, якщо воно є, що може порушити зв'язок з іншими даними.


1
Ага так - я шукаю те, що не позбудеться попередніх посвідчень особи
thekevinscott

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


1

Я зіткнувся з проблемою, коли ПІДКЛЮЧЕНО КЛЮЧИТИ ОНОВЛЕННЯ id = LAST_INSERT_ID (id) збільшення первинного ключа на 1. Тож ідентифікатор наступного входу в сеансі буде збільшений на 2


0

Варто зауважити, і це може бути очевидним (але я все одно скажу це для наочності тут), що ЗАМОВЛЕННЯ знищить існуючий відповідний рядок перед тим, як вставити нові дані. ON DUPLICATE KEY UPDATE оновить лише вказані стовпці та збереже рядок.

З посібника :

REPLACE працює точно так само, як INSERT, за винятком випадків, коли старий рядок у таблиці має те саме значення, що й новий рядок для PRIMARY KEY або UNIQUE index, старий рядок видаляється перед тим, як вставити новий рядок.


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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.