ВСТАВИТЬ НА ОБНОВЛЕНИЕ ДУБЛИКАЦИИ КЛЮЧА с ГДЕ?

Я делаю INSERT ... ON DUPLICATE KEY UPDATE, но мне нужно, чтобы часть обновления была условной, выполняя обновление только в том случае, если изменилось какое-то дополнительное условие.

Однако WHERE не разрешено на этом UPDATE. Есть ли обходной путь для этого?

Я не могу использовать комбинации INSERT/UPDATE/SELECT, так как это должно работать над репликацией.


person Andreas Wederbrand    schedule 18.03.2010    source источник


Ответы (6)


Я предлагаю вам использовать IF() для этого.

См.: условные-дупликации-key-updates-with-mysql

INSERT INTO daily_events (created_on, last_event_id, last_event_created_at)
  VALUES ('2010-01-19', 23, '2010-01-19 10:23:11')
ON DUPLICATE KEY UPDATE
  last_event_id = IF(last_event_created_at < VALUES(last_event_created_at), VALUES(last_event_id), last_event_id);
person LouisXIV    schedule 19.12.2012
comment
Я никогда не знал, что могу получить доступ к исходному значению таблицы внутри части UPDATE. Это, вероятно, решает мою проблему (хотя моя старая работа довольно хороша :)) - person Andreas Wederbrand; 20.12.2012
comment
Может случиться так, что MySQL будет жаловаться на «столбец в списке полей неоднозначен» - тогда вам нужно будет использовать: ON DUPLICATE KEY UPDATE daily_events.last_event_id = IF(daily_events.last_event_created_at < VALUES(last_event_created_at), VALUES(last_event_id), daily_events.last_event_id); - person boryn; 14.02.2014
comment
Ссылка, размещенная на thewebfellas.com, к сожалению, мертва. - person Taylor R; 04.04.2017
comment
Ссылка, размещенная на thewebfellas.com, теперь действительна. - person Razvan_TK9692; 01.03.2020

Это наше окончательное решение, работает как шарм!

игнорирование вставки гарантирует, что строка существует как на ведущем, так и на подчиненном устройстве, если они когда-либо перенаправлялись.

update... where гарантирует, что только самое последнее глобальное обновление будет конечным результатом после выполнения всей репликации.

mysql> desc test;
+-------+--------------+------+-----+-------------------+-------+
| Field | Type         | Null | Key | Default           | Extra |
+-------+--------------+------+-----+-------------------+-------+
| id    | int(11)      | NO   | PRI | NULL              |       | 
| value | varchar(255) | YES  |     | NULL              |       | 
| ts    | timestamp    | NO   |     | CURRENT_TIMESTAMP |       | 
+-------+--------------+------+-----+-------------------+-------+

mysql> insert ignore into test values (4, "foo", now());    
mysql> update test set value = "foo", ts = now() where id = 4 and ts <= now();
person Andreas Wederbrand    schedule 18.03.2010
comment
Объяснение в ответе @lexu: два утверждения, первое — insert ignore, второе — что-то вроде update ignore. - person xmedeko; 28.02.2017
comment
Похоже, что второй оператор просто выполняет update test set value = "foo", ts = now() where id = 4 and ts < now(), только очень запутанным образом. - person antak; 23.01.2018
comment
Да вы правы. После публикации этого ответа я изменил код на insert ignore..., за которым следует update .... where ts < now() - person Andreas Wederbrand; 23.01.2018
comment
@AndreasWederbrand, не могли бы вы соответствующим образом обновить свой ответ, чтобы другие тоже могли извлечь пользу из ваших выводов? - person Eliran Malka; 26.08.2018

вы можете использовать два оператора вставки ... так как вы МОЖЕТЕ добавить предложение where в часть выбора для исходных данных.

выберите два набора данных, один из которых вы вставите с «дубликатом», а другой будет вставлен без «дубликата».

person lexu    schedule 18.03.2010

Обзор

  • AWUpsertCondy хочет изменить ДО на ПОСЛЕ

Иллюстрация упрощенной задачи

Проблема

  • AWUpsertCondy не хочет, чтобы запрос на вставку завершался ошибкой, если MySQL обнаруживает повторяющийся первичный ключ.
  • MySQL не поддерживает условное предложение WHERE с ON DUPLICATE KEY UPDATE

Решение

  • MySQL поддерживает условное предложение с функцией IF()
  • Здесь у нас есть простое условие для обновления только тех элементов с идентификатором пользователя меньше 9.
INSERT INTO zzdemo_table02
    (lname,userid)
  SELECT
    lname,userid
    FROM(
      SELECT
        lname,userid
      FROM
        zzdemo_table01
    ) as tt01
ON DUPLICATE KEY UPDATE
    userid=IF(@doupdate:=IF( (tt01.userid < 9) , True, False), 
        tt01.userid, zzdemo_table02.userid)
    ,lname=IF(@doupdate, tt01.lname , zzdemo_table02.lname )
;

Подводные камни

  • Мы вводим переменную MySQL @doupdate, чтобы отметить, соответствует ли строка UPDATE условию. Затем мы используем эту же переменную для всех столбцов базы данных, которые мы используем в операторе UPDATE.
  • В первом условном выражении мы объявляем переменную и определяем, применяется ли условное выражение. Этот подход, возможно, более громоздкий, чем предложение WHERE.

Смотрите также

person dreftymac    schedule 12.06.2019

таблица php_lock:
name:idString, locked:bool, time:timestamp, locked_by:string
значения для вставки или обновления
1, CURRENT_TIMESTAMP, 'script'
где name='wwww' AND locked =2

INSERT INTO `php_lock` (`name`, locked, `time`, `locked_by`)  
(SELECT * FROM 
    (SELECT `name`,1,CURRENT_TIMESTAMP, 'script' FROM `php_lock` 
        WHERE `name`='wwww' AND `locked`=2  
    UNION (
    SELECT 'wwww',1 ,CURRENT_TIMESTAMP, 'script') 
) AS temp LIMIT 1) 
ON DUPLICATE KEY UPDATE locked=VALUES(locked), `time`=VALUES(`time`), `locked_by`=VALUES(`locked_by`);
person Tito100    schedule 16.08.2013

On duplicate key не позволяет нам использовать предложение where, поэтому есть две альтернативы для достижения одного и того же.

  1. Если вы знаете, что большую часть времени вы получите дубликат ключа, тогда

    a. Update the table first using update query and where clause b. If update fails then insert the record into table using insert query

  2. Если вы знаете, что большую часть времени вы собираетесь вставлять в таблицу, тогда

    a. Insert the record into table using insert ignore query - what it does is actually ignore the insert if duplicate key found b. If insert ignore fails then update the record using update query

Для справки по insert ignore нажмите здесь

person Ranjit Singh    schedule 15.01.2017
comment
Как insert ignore может потерпеть неудачу? - person Andreas Wederbrand; 16.01.2017
comment
если вы попытаетесь вставить такой же уникальный ключ, insert ignore не удастся. - person Ranjit Singh; 17.01.2017
comment
Нет, это то, что делает ignore. Он игнорирует исключение дублирующего ключа. - person Andreas Wederbrand; 17.01.2017