Подтвердить что ты не робот

MySQL Trigger после обновления только в том случае, если строка изменилась

Есть ли возможность использовать триггер "после обновления" только в том случае, если данные были ДЕЙСТВИТЕЛЬНО изменены. Я знаю о "НОВЫХ и СТАРЫХ". Но при их использовании я могу сравнивать столбцы. Например, "NEW.count < > OLD.count".

Но мне нужно что-то вроде: запустить триггер, если "NEW < > OLD"

Пример:

create table foo (a INT, b INT);
create table bar (a INT, b INT);

INSERT INTO foo VALUES(1,1);
INSERT INTO foo VALUES(2,2);
INSERT INTO foo VALUES(3,3);

CREATE TRIGGER ins_sum
    AFTER UPDATE ON foo
    FOR EACH ROW
    INSERT INTO bar VALUES(NEW.a, NEW.b);

UPDATE foo SET b = 3 WHERE a=3;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1  Changed: 0  Warnings: 0


select * from bar;
+------+------+
| a    | b    |
+------+------+
|    3 |    3 |
+------+------+

Дело в том, что было обновление, но ничего не изменилось. Но спусковой механизм все равно. ИМХО должно быть так, как нет.

Я знаю, что я мог бы использовать

IF NOW.b < > OLD.b

для этого примера.

НО представьте себе большую таблицу с меняющимися столбцами. Вы должны сравнить каждый столбец, и если в базе данных изменится настройка триггера. И не "хорошо" сравнивать каждый столбец строки с жестким кодом:)

Добавление

Как вы можете видеть на линии

Соответствие строк: 1 Изменено: 0 Предупреждения: 0

MySQL знает, что строка не изменилась. Но он не делится этими знаниями с помощью триггера. Триггер типа "ПОСЛЕ РЕАЛЬНОГО ОБНОВЛЕНИЯ" или что-то в этом роде будет круто.

4b9b3361

Ответ 1

В качестве обходного пути вы можете использовать временную метку (старую и новую) для проверки, но она не обновляется, если в строке нет изменений. (Возможно, это источник путаницы? Потому что этот тоже называется "при обновлении", но не выполняется, когда не происходит никаких изменений) Изменения в течение одной секунды не будут выполнять эту часть триггера, но в некоторых случаях это может быть хорошо (например, когда у вас есть приложение, которое все равно отклоняет быстрые изменения.)

Например, вместо

IF NEW.a <> OLD.a or NEW.b <> OLD.b /* etc, all the way to NEW.z <> OLD.z */ 
THEN  
  INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b) ;
END IF

вы можете использовать

IF NEW.ts <> OLD.ts 
THEN  
  INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b) ;
END IF

Тогда вам не нужно менять триггер при каждом обновлении схемы (вопрос, который вы упомянули в вопросе.)

EDIT: добавлен полный пример

create table foo (a INT, b INT, ts TIMESTAMP);
create table bar (a INT, b INT);

INSERT INTO foo (a,b) VALUES(1,1);
INSERT INTO foo (a,b) VALUES(2,2);
INSERT INTO foo (a,b) VALUES(3,3);

DELIMITER ///

CREATE TRIGGER ins_sum AFTER UPDATE ON foo
    FOR EACH ROW
    BEGIN
        IF NEW.ts <> OLD.ts THEN  
            INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b);
        END IF;
    END;
///

DELIMITER ;

select * from foo;
+------+------+---------------------+
| a    | b    | ts                  |
+------+------+---------------------+
|    1 |    1 | 2011-06-14 09:29:46 |
|    2 |    2 | 2011-06-14 09:29:46 |
|    3 |    3 | 2011-06-14 09:29:46 |
+------+------+---------------------+
3 rows in set (0.00 sec)

-- UPDATE without change
UPDATE foo SET b = 3 WHERE a = 3;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1  Changed: 0  Warnings: 0

-- the timestamo didnt change
select * from foo WHERE a = 3;
+------+------+---------------------+
| a    | b    | ts                  |
+------+------+---------------------+
|    3 |    3 | 2011-06-14 09:29:46 |
+------+------+---------------------+
1 rows in set (0.00 sec)

-- the trigger didn't run
select * from bar;
Empty set (0.00 sec)

-- UPDATE with change
UPDATE foo SET b = 4 WHERE a=3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

-- the timestamp changed
select * from foo;
+------+------+---------------------+
| a    | b    | ts                  |
+------+------+---------------------+
|    1 |    1 | 2011-06-14 09:29:46 |
|    2 |    2 | 2011-06-14 09:29:46 |
|    3 |    4 | 2011-06-14 09:34:59 |
+------+------+---------------------+
3 rows in set (0.00 sec)

-- and the trigger ran
select * from bar;
+------+------+---------------------+
| a    | b    | ts                  |
+------+------+---------------------+
|    3 |    4 | 2011-06-14 09:34:59 |
+------+------+---------------------+
1 row in set (0.00 sec)

Он работает из-за поведения mysql при обработке временных меток. Временная метка обновляется только в том случае, если в обновлениях произошли изменения.

Документация находится здесь:
https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html

desc foo;
+-------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type      | Null | Key | Default           | Extra                       |
+-------+-----------+------+-----+-------------------+-----------------------------+
| a     | int(11)   | YES  |     | NULL              |                             |
| b     | int(11)   | YES  |     | NULL              |                             |
| ts    | timestamp | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------+-----------+------+-----+-------------------+-----------------------------+

Ответ 2

НО представьте себе большую таблицу с меняющимися столбцами. Вы должны сравнить каждый столбец, и если в базе данных изменится настройка триггера. И он не "чувствует" себя хорошо, чтобы сравнить каждую строку с жестким кодом:)

Да, но это способ продолжить.

В качестве побочного примечания также рекомендуется проверить предварительную проверку перед обновлением:

UPDATE foo SET b = 3 WHERE a=3 and b <> 3;

В вашем примере это приведет к обновлению (и, следовательно, переписать) две строки вместо трех.

Ответ 3

Я не могу комментировать, так что будьте осторожны, если в столбце поддерживаются значения NULL, OLD.x < > NEW.x не достаточно, потому что

SELECT IF(1<>NULL,1,0)

возвращает 0 так же, как

NULL<>NULL 1<>NULL 0<>NULL 'AAA'<>NULL

Таким образом, он не будет отслеживать изменения FROM и TO NULL

Правильный способ в этом сценарии -

((OLD.x IS NULL AND NEW.x IS NOT NULL) OR (OLD.x IS NOT NULL AND NEW.x IS NULL) OR (OLD.x<>NEW.x))

Ответ 4

Вы можете сделать это, сравнив каждое поле, используя NULL-безопасный оператор равенства <=>, а затем отрицая результат используя NOT.

Полный триггер станет следующим:

DROP TRIGGER IF EXISTS `my_trigger_name`;

DELIMITER $$

CREATE TRIGGER `my_trigger_name` AFTER UPDATE ON `my_table_name` FOR EACH ROW 
    BEGIN
        /*Add any fields you want to compare here*/
        IF !(OLD.a <=> NEW.a AND OLD.b <=> NEW.b) THEN
            INSERT INTO `my_other_table` (
                `a`,
                 `b`
            ) VALUES (
                NEW.`a`,
                NEW.`b`
            );
        END IF;
    END;$$

DELIMITER ;

(Основываясь на другом ответе.)

Ответ 5

Здесь, если какая-либо строка влияет на новую вставку Затем она будет обновляться в другой таблице в базе данных.

DELIMITER $$

CREATE TRIGGER "give trigger name" AFTER INSERT ON "table name" 
FOR EACH ROW
BEGIN
    INSERT INTO "give table name you want to add the new insertion on previously given table" (id,name,age) VALUES (10,"sumith",24);
END;
$$
DELIMITER ;

Ответ 6

Используйте следующий запрос, чтобы увидеть, какие строки имеют изменения:

(select * from inserted) except (select * from deleted)

Результаты этого запроса должны состоять из всех новых записей, отличных от старых.

Ответ 7

MYSQL TRIGGER BEFORE UPDATE IF OLD.a<>NEW.b

USE `pdvsa_ent_aycg`;

DELIMITER $$

CREATE TRIGGER `cisterna_BUPD` BEFORE UPDATE ON `cisterna` FOR EACH ROW

BEGIN

IF OLD.id_cisterna_estado<>NEW.id_cisterna_estado OR OLD.observacion_cisterna_estado<>NEW.observacion_cisterna_estado OR OLD.fecha_cisterna_estado<>NEW.fecha_cisterna_estado

    THEN 

        INSERT INTO cisterna_estado_modificaciones(nro_cisterna_estado, id_cisterna_estado, observacion_cisterna_estado, fecha_cisterna_estado) values (NULL, OLD.id_cisterna_estado, OLD.observacion_cisterna_estado, OLD.fecha_cisterna_estado); 

    END IF;

END