web-dev-qa-db-ja.com

行が変更された場合のみ更新後のMySQLトリガー

データが本当に変更された場合にのみ、「更新後」トリガーを使用する可能性はありますか。 「新旧」を知っています。しかし、それらを使用する場合、列を比較することしかできません。たとえば、「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は、行が変更されなかったことを知っています。しかし、この知識をトリガーと共有することはありません。 「AFTER REAL UPDATE」などのトリガーまたはこのようなものはクールです。

62
jens

回避策として、チェックにタイムスタンプ(古いものと新しいもの)を使用できます。行に変更がない場合、タイムスタンプは更新されませんnot。 (おそらくそれが混乱の原因ですか?それは「更新時」とも呼ばれますが、変更が発生しないと実行されないためです)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

そうすれば、スキーム(質問で言及した問題)を更新するたびにトリガーを変更する必要がなくなります。

編集:完全な例を追加

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 |
+-------+-----------+------+-----+-------------------+-----------------------------+
70
Inca

しかし、列が変わる大きなテーブルを想像してください。すべての列を比較する必要があり、データベースが変更された場合はトリガーを調整する必要があります。そして、ハードコーディングされたすべての行を比較するのは「気分が悪い」:)

ええ、しかしそれが先へ進む方法です。

補足として、更新する前に先制的にチェックすることも良い習慣です:

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

あなたの例では、これはそれを更新します(したがってoverwritetwo行ではなく3行。

15

コメントできないため、列が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))
12
Wax Cage

これを行うには、 NULL-safe equals operator <=> を使用して各フィールドを比較し、次に 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 ;

私の別の答え に基づきます。)

9
user2428118

ここで、新しい挿入に影響する行がある場合、データベース内の別のテーブルで更新されます。

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 ;
2

次のクエリを使用して、変更のある行を確認します。

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

このクエリの結果は、古いレコードとは異なるすべての新しいレコードで構成される必要があります。

1
Hawthorne
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
0
Herwin Rey