MySQL Trigger после обновления, только если строка изменилась - PullRequest
62 голосов
/ 09 июня 2011

Есть ли возможность использовать триггер «после обновления» только в том случае, если данные ДЕЙСТВИТЕЛЬНО изменены.Я знаю о "НОВОМ и СТАРЫМ".Но при их использовании я могу сравнивать только столбцы.Например, «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 |
+------+------+

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

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

ЕСЛИ СЕЙЧАС.b <> OLD.b

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

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

Добавление

Как видно из строки

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

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

Ответы [ 7 ]

69 голосов
/ 10 июня 2011

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

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

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

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

РЕДАКТИРОВАТЬ: Добавлен полный пример

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 |
+-------+-----------+------+-----+-------------------+-----------------------------+
15 голосов
/ 09 июня 2011

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

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

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

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

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

12 голосов
/ 13 января 2014

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

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

возвращает 0 как то же самоеas

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))
9 голосов
/ 06 июня 2014

Вы можете сделать это, сравнивая каждое поле, используя 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 ;

(на основе другого моего ответа .)

2 голосов
/ 16 ноября 2015

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

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 ;
1 голос
/ 03 августа 2016

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

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

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

0 голосов
/ 04 февраля 2016
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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...