ПрограммированиеBackend разработчик

Объясните различия между командами UPDATE, MERGE и INSERT ... ON DUPLICATE KEY UPDATE в SQL. В каких случаях какой подход предпочтительнее, и как эти инструменты влияют на производительность и целостность данных?

Проходите собеседования с ИИ помощником Hintsage

Ответ

SQL предоставляет различные способы обновления или вставки данных в таблицу:

  • UPDATE — изменяет существующие записи, соответствующие условию.
  • MERGE (или UPSERT) — объединяет логику вставки и обновления в одной команде: если строка найдена — обновляет; если нет — добавляет.
  • INSERT ... ON DUPLICATE KEY UPDATE (MySQL) или INSERT ... ON CONFLICT DO UPDATE (PostgreSQL) — вставляет новую строку, а при конфликте по ключу обновляет существующую.

Когда и что использовать:

  • Если точно известно, что записей ещё нет — используйте INSERT.
  • Для обновления существующих записей — UPDATE с условиями.
  • Если не известно, существует ли запись, эффективнее использовать MERGE или UPSERT — это экономит сложность и уменьшает количество SQL-запросов.
  • UPSERT-операции обычно быстрее при большом количестве записей и минимизируют гонки.

Пример (PostgreSQL):

INSERT INTO employees(id, name, salary) VALUES (1, 'Ivan', 100000) ON CONFLICT (id) DO UPDATE SET salary = EXCLUDED.salary;

Влияние на производительность и целостность:

  • Множественные отдельные UPDATE/INSERT могут привести к гонкам и блокировкам.
  • MERGE/UPSERT эффективнее при массовой миграции и поддерживают атомарность.

Вопрос с подвохом

Что произойдет, если попытаться выполнить UPSERT на уникальном поле, но в одной и той же транзакции одновременно вставить и обновить одну и ту же строку?

Правильный ответ: Возможно возникновение ошибки блокировки/конфликта, т.к. транзакция попытается изменить одну и ту же строку дважды. Операция не завершится или завершится ошибкой deadlock в зависимости от СУБД.

Пример:

BEGIN; INSERT INTO users(id, name) VALUES (1, 'Oleg') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name; UPDATE users SET name = 'Petr' WHERE id = 1; COMMIT;

При неправильном порядке и уровне изоляции возможен конфликт изменений.


История №1

В одной логике миграции данных использовали комбинацию SELECT, а затем INSERT или UPDATE, что иногда приводило к тому, что другая транзакция устанавливала данные раньше, и происходила ошибка «duplicate key». Это вызвало частые сбои ночного экспорта данных, потребовалось заменить логику на UPSERT.


История №2

В проекте на MySQL некорректно применяли ON DUPLICATE KEY UPDATE, который некорректно учитывал составные уникальные ключи. В результате часть данных не обновлялась, а появлялись дубликаты. Проблему удалось выявить только на боевом стенде.


История №3

В проекте использовали UPDATE вместо UPSERT для синхронизации данных пользователя. При сбоях соединения происходила потеря части данных, поскольку обновление пропускалось для новых пользователей. После анализа полностью внедрили MERGE/UPSERT.