Страницы

Поиск по вопросам

вторник, 16 июля 2019 г.

UPDATE, если значение изменилось, INSERT, если значения нет

Имеется таблица в MySQL:
id | source | source_id | time | status | rating ---------------------------------------------------- 1 | 1 | 123 | 12:10 | wait | 1000 2 | 1 | 456 | 12:15 | done | 2000 3 | 2 | 123 | 12:20 | wait | 3000
id - это уникальный первичный ключ source - источник откуда поступили данные source_id - id данных под которым их знает источник time - это CURRENT_TIMESTAMP()
source и source_id не уникальны, но для конкретного source всегда уникален source_id
В поступающих данных имеется только source, source_id, status, rating. rating - постоянно меняется.

При данных
source | source_id | status | rating ------------------------------------- 1 | 123 | wait | 55555
Ничего не должно произойти- штамп времени и rating не должны измениться, это нужно потому что при изменении штампа времени на сервере все измененные записи отправляются на клиентские машины, но rating абсолютно не важен, важно только изменение status

При данных
source | source_id | status | rating ------------------------------------- 1 | 123 | done | 66666
Должен быть выполнен UPDATE полей time, status, rating, потому что status изменился и нужно разослать записи клиентам.

При данных
source | source_id | status | rating ------------------------------------- 3 | 123 | done | 77777
Должен быть выполнен INSERT новой записи.

Как это сделать мне подсказал @Visman в ответе ниже: https://ru.stackoverflow.com/a/452279/481
Данное решение делает все как нужно, но в идеале хотелось бы, чтобы UPDATE совсем не происходил, если status не изменился (в указанном решении происходит UPDATE на те же самые значения, что уже есть на текущий момент).


Ответ

Ответ делал для первого варианта вопроса, но смысл не слишком меняется и для второго, только больше переменных нужно указать.
Таблица для теста:
CREATE TABLE `table` ( `id` INT NOT NULL AUTO_INCREMENT, `time` TIMESTAMP, `status` varchar(55), PRIMARY KEY (id) );
INSERT INTO `table` (`id`, `time`, `status`) VALUES (1, FROM_UNIXTIME(1000), 'wait'), (2, FROM_UNIXTIME(10000), 'success') ;
Наглядно
id time status
1 January, 01 1970 00:16:40 wait 2 January, 01 1970 02:46:40 success
Запрос получается такой (Переменные вынесены отдельно для наглядности, хотя можно их задавать прямо в текст запроса).
http://sqlfiddle.com/#!9/c2d1c/1
SET @id=3, @status='wait'; insert into `table` (`id`, `time`, `status`) values (@id, CURRENT_TIMESTAMP, @status) on duplicate key update `status`=@status, `time`=( SELECT IFNULL(`t2`.`time`, CURRENT_TIMESTAMP) FROM `table` `t1` LEFT JOIN `table` `t2` ON `t2`.`id`=@id AND `t2`.`status`=@status WHERE `t1`.`id`=@id );
Получается такой результат:
id time status
1 January, 01 1970 00:16:40 wait 2 January, 01 1970 02:46:40 success 3 September, 20 2015 14:07:17 done http://sqlfiddle.com/#!9/37337/1
Переменные изменены на входе
SET @id=1, @status='wait';
Результат
id time status
1 January, 01 1970 00:16:40 wait 2 January, 01 1970 02:46:40 success http://sqlfiddle.com/#!9/43a82/1
Переменные
SET @id=1, @status='done';
Результат
id time status
1 September, 20 2015 14:08:35 done 2 January, 01 1970 02:46:40 success
P.S. Тут статья про запросы вида INSERT… ON DUPLICATE KEY UPDATE и AUTO_INCREMENT и их поведение.
UPD
Запрос для вопроса из вашего комментария
CREATE TABLE `table` ( `id` INT NOT NULL AUTO_INCREMENT, `source` INT NOT NULL, `source_id` INT NOT NULL, `time` TIMESTAMP, `status` varchar(55), `rating` INT NOT NULL, PRIMARY KEY (id), UNIQUE KEY (`source`, `source_id`) );
INSERT INTO `table` (`id`, `source`, `source_id`, `time`, `status`, `rating`) VALUES (1, 1, 123, FROM_UNIXTIME(1000), 'wait', 1000), (2, 1, 456, FROM_UNIXTIME(2000), 'done', 2000), (3, 2, 123, FROM_UNIXTIME(3000), 'wait', 3000) ;
SET @source=1, @source_id=123, @status='wait', @rating=555; insert into `table` (`source`, `source_id`, `time`, `status`, `rating`) values (@source, @source_id, CURRENT_TIMESTAMP, @status, @rating) on duplicate key update `time`=( SELECT IFNULL(`t2`.`time`, CURRENT_TIMESTAMP) FROM `table` `t1` LEFT JOIN `table` `t2` ON `t2`.`source`=@source AND `t2`.`source_id`=@source_id AND `t2`.`status`=@status WHERE `t1`.`source`=@source AND `t1`.`source_id`=@source_id ), `status`=@status, `rating`=IF(`time`<>CURRENT_TIMESTAMP, `rating`, @rating);
Тест http://sqlfiddle.com/#!9/921be/1
Так как вы id не используете, то уникальность проверяем по паре source и source_id. rating и другие столбцы (если появятся) изменяем в зависимости от time через условие IF(time<>CURRENT_TIMESTAMP, ...) чтобы не плодить вложенные запросы.

Комментариев нет:

Отправить комментарий