INSERT ... ON DUPLICATE KEY UPDATE
Знакомимся с upsert по-MySQL: одна команда «вставь, а если уже есть — обнови».
Upsert — операция «вставить новую строку или, если такая уже существует, обновить её», выполняемая одним запросом.
Зачем это нужно
Частая задача: «увеличить счётчик просмотров» или «сохранить настройку». Если делать вручную, придётся сначала SELECT, проверить наличие, потом INSERT или UPDATE — три шага и гонка состояний при параллельных запросах. MySQL решает это одной командой.
ON DUPLICATE KEY UPDATE
Конструкция работает, когда на таблице есть PRIMARY KEY или UNIQUE-ограничение. При попытке вставить строку, конфликтующую по такому ключу, вместо ошибки выполняется указанное обновление.
CREATE TABLE page_views (
page VARCHAR(255) PRIMARY KEY,
views INT NOT NULL DEFAULT 0
);
-- если страницы ещё нет — вставит views=1,
-- если есть — увеличит счётчик на 1
INSERT INTO page_views (page, views)
VALUES ('/home', 1)
ON DUPLICATE KEY UPDATE views = views + 1;
Это MySQL-специфика (для чтения). Функция VALUES(col) внутри части UPDATE даёт значение, которое пытались вставить:
INSERT INTO settings (user_id, theme)
VALUES (42, 'dark')
ON DUPLICATE KEY UPDATE theme = VALUES(theme);
Родственные приёмы
| Команда | Поведение при конфликте ключа |
INSERT | ошибка |
INSERT IGNORE | тихо пропустить конфликтующую строку |
INSERT ... ON DUPLICATE KEY UPDATE | обновить существующую строку |
REPLACE | удалить старую строку и вставить новую |
Осторожно с REPLACE: он физически удаляет старую строку (срабатывают ON DELETE внешних ключей, теряются незаданные поля) и вставляет заново. ON DUPLICATE KEY UPDATE обычно безопаснее, потому что обновляет на месте.
Что переносимо в песочнице
Сам синтаксис ON DUPLICATE KEY UPDATE — чисто MySQL и в SQLite не выполнится. Но саму идею «было одно значение — стало другое» можно показать обычным UPDATE:
CREATE TABLE page_views (
page TEXT PRIMARY KEY,
views INTEGER NOT NULL DEFAULT 0
);
INSERT INTO page_views (page, views) VALUES ('/home', 1);
-- «повторный визит»: имитируем инкремент счётчика
UPDATE page_views SET views = views + 1 WHERE page = '/home';
SELECT page, views FROM page_views;
Вывод:
page views /home 2
Итог
INSERT ... ON DUPLICATE KEY UPDATE— атомарный upsert по уникальному ключу.- Работает только при наличии
PRIMARY KEYилиUNIQUEна таблице. INSERT IGNOREмолча пропускает конфликт,REPLACEудаляет и вставляет заново.REPLACEопаснее: он реально удаляет строку — предпочитайтеON DUPLICATE KEY UPDATE.