MySQL хороший способ вставить строку, если она не найдена, или обновить ее, если она найдена

Очень часто мне нужно выполнить запрос для одного из моих пользователей, где я хочу, чтобы строка сохранялась и ассоциировалась с этим пользователем в отношении 1-к-1. Итак, скажем (это всего лишь произвольный пример), что у меня есть таблица, которая отслеживает автомобиль пользователя, а также некоторую информацию об автомобиле. Каждый пользователь может иметь либо 0, либо 1 автомобиль. Если у пользователя нет автомобиля, для этого пользователя нет записи в таблице.

таблица cars (опять же просто пример): id, user_id, car_make, car_model

Итак, когда я обновляю эту таблицу, я всегда делаю что-то вроде этого (псевдокод):

result = SELECT * FROM cars WHERE user_id=5
if (num_rows(result)>0){
    UPDATE cars SET car_make='toyota', car_model='prius' WHERE user_id=5
}else{
    INSERT INTO cars (user_id, car_make, car_model) VALUES (5, 'toyota', 'prius')
}

Как я могу превратить это в одно элегантное утверждение, которое работает «атомарно»? Что произойдет, если в другом процессе будет УДАЛЕН ряд между операторами SELECT и UPDATE? Моя инструкция UPDATE завершится ошибкой там, где должна была выполняться инструкция INSERT. И я чувствую, что мне нужно сделать два похожих (но разных) утверждения, чтобы выполнить одно и то же! Мне нужно какое-то утверждение, которое убедит меня, что нужные мне данные существуют в таблице, особенно когда мне нужна только 1 строка, удовлетворяющая моему требованию. Например, это может быть что-то вроде (это, конечно, полностью выдумано):

MAKE SURE A ROW IN cars WHERE user_id=5 IS SET WITH car_make='toyota', car_model='prius'

Таким образом, если user_id из 5 уже существует, он будет обновлен, в противном случае он будет вставлен. Кроме того, если бы я изменил требования, например, чтобы сказать, что каждый пользователь может иметь ноль или одну машину данного car_make, то я мог бы дополнительно указать, что:

MAKE SURE A ROW IN cars WHERE user_id=5 AND car_make='toyota' IS SET WITH car_model='prius'

Надеюсь, мой вопрос имеет смысл! Как я могу улучшить эту базовую операцию «вставить, если не найдено» или «обновить, если найдено», которая возникает так часто? Спасибо за любую помощь!


person DivideByHero    schedule 18.04.2009    source источник


Ответы (6)


Вы можете использовать "REPLACE INTO" или "ВСТАВИТЬ… ПРИ ОБНОВЛЕНИИ Дубликата ключа". Я считаю, что второе — это то, что вам нужно, но бывают ситуации, когда REPLACE INTO удобен.

person mishac    schedule 18.04.2009

Из другого моего ответа на переполнение стека :

Если вы хотите сделать это в одном выражении, я бы рекомендовал использовать синтаксис INSERT ... ON DUPLICATE KEY UPDATE следующим образом:

INSERT INTO table (id, someothervalue) VALUES (1, 'hi mom')
  ON DUPLICATE KEY UPDATE someothervalue = 'hi mom';

Исходный оператор INSERT будет выполняться, если нет существующей записи с указанным значением ключа (либо первичным ключом, либо уникальным). Если запись уже существует, выполняется следующий оператор UPDATE (someothervalue = 3).

Это поддерживается во всех версиях MySQL. Для получения дополнительной информации см. страницу Справочного руководства по MySQL для INSERT ... ON DUPLICATE KEY UPDATE

person defines    schedule 25.06.2009

Это называется UPSERT (UPдата или inSERT). Есть ряд вопросов SO об этом, которые вы можете найти. См. Википедию

EDIT: MySQL 4.1+ поддерживает INSATE (INSert или updATE), что должно дать вам то же самое, если у вас есть первичный ключ. Руководство по MySQL

person Andrew Barnett    schedule 18.04.2009

Я думаю проще поменять местами. Попробуй вставить, потом обнови.

MySQL специально имеет пункт «ON DUPLICATE KEY»

ВСТАВИТЬ В автомобили (поля) ЗНАЧЕНИЯ (значения) ON DUPLICATE KEY UPDATE...

Это, конечно, требует, чтобы у вас была правильная настройка уникальных ключей.

person Darryl E. Clarke    schedule 18.04.2009

В Oracle у вас есть слияние.

Понятия не имею о MySql, но этот термин может дать вам что-то еще для поиска.

person juan    schedule 18.04.2009

пример того, как я использовал ON DUPLICATE KEY UPDATE: INSERT INTO реестр (имя, значение) VALUES ('" . $key. "', '" . $val. "') ON DUPLICATE KEY UPDATE value= '" . $ вал . "'"

person Community    schedule 18.04.2009