Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як зробити upsert у MySQL через INSERT ... ON DUPLICATE KEY UPDATE?

Upsert - вставити рядок, а якщо він уже є, - оновити. У MySQL «вже є» визначається порушенням унікального ключа (первинного чи UNIQUE).

INSERT INTO product_stats (product_id, views)
VALUES (42, 1)
ON DUPLICATE KEY UPDATE views = views + 1;

Перший виклик вставить рядок, наступні - збільшать лічильник. Атомарно, без гонки «перевірити - вставити».

Посилання на значення, яке намагалися вставити - через псевдонім рядка (MySQL 8.0.19+):

INSERT INTO prices (sku, price, updated_at)
VALUES ('A1', 199.00, NOW()), ('B2', 349.00, NOW()) AS new
ON DUPLICATE KEY UPDATE price = new.price, updated_at = new.updated_at;

Стара функція VALUES(price) в UPDATE-частині працює, але застаріла.

Підводні камені:

  • Потрібен унікальний ключ саме на тих колонках, за якими визначається «той самий» рядок. Без нього буде звичайна вставка дубліката.
  • Кілька унікальних ключів: якщо рядок конфліктує одразу з двома різними рядками за різними ключами, оновиться лише один - результат несподіваний. Upsert краще робити по таблиці з одним унікальним ключем, крім первинного.
  • Автоінкремент «з'їдається»: невдала вставка часто все одно забирає значення AUTO_INCREMENT - звідси дірки в ID.
  • Кількість змінених рядків: 1 - вставлено, 2 - оновлено, 0 - значення ті самі.
  • Блокування: при конфлікті InnoDB бере блокування на запис індексу - при великій конкуренції можливі deadlock'и, і їх варто повторювати.

У Laravel: Model::upsert($rows, uniqueBy: ['sku'], update: ['price', 'updated_at']) генерує саме цей запит для MySQL (і ON CONFLICT для PostgreSQL).

Докладніше в документації: INSERT ... ON DUPLICATE KEY UPDATE

1

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання