MySQL触发器在同一个表中的一个字段上工作,而不在第二个字段上工作

时间:2017-02-22 23:57:42

标签: mysql triggers

我有一个带有表inventory的MySQL数据库,其中设置了多个触发器以捕获第二个表inventory_history中的更改。我更新了两个字段(包括单个查询和两个单独的查询),触发器始终只对两个字段中的一个字段起作用(qty但不在last_sale上)。

这是一个麻烦的问题:

UPDATE inventory SET last_sale = 321, qty = 0 WHERE id = 123; 

或者,这些查询组合也不起作用:

UPDATE inventory SET last_sale = 321 WHERE id = 123; 
UPDATE inventory SET qty = 0 WHERE id = 123; 

以下是表格构造和触发器:

CREATE TABLE `inventory` (
  `serial_no` varchar(255) DEFAULT NULL,
  `qty` mediumint(9) DEFAULT NULL,
  `partid` mediumint(9) unsigned DEFAULT NULL,
  `last_sale` mediumint(9) unsigned DEFAULT NULL,
  `date_created` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `id` mediumint(9) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`),
  KEY `partid` (`partid`),
  KEY `date_created` (`date_created`),
  KEY `last_sale` (`last_sale`),
  KEY `last_rma` (`last_return`),
  KEY `last_purchase` (`last_purchase`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `inventory_history` (
  `date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `inventory_id` mediumint(9) unsigned NOT NULL,
  `field_changed` enum('serial_no','qty','partid','last_sale','new') NOT NULL,
  `changed_from` varchar(255) NOT NULL,
  KEY `inventory_id` (`inventory_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TRIGGER `inv_new` AFTER INSERT ON `inventory`
 FOR EACH ROW BEGIN
  SET
    @id = NEW.id,
    @userid = NEW.userid,
    @date = now();

  INSERT INTO inventory_history VALUES (@date,@userid, @id, 'new', 'new');
END
//
DELIMITER ;
DROP TRIGGER IF EXISTS `inv_update`;
DELIMITER //
CREATE TRIGGER `inv_update` AFTER UPDATE ON `inventory`
 FOR EACH ROW BEGIN
  SET @userid = OLD.userid;
  SET @inv_id = OLD.id;

  IF (OLD.serial_no <> NEW.serial_no) THEN
    INSERT INTO inventory_history VALUES (now(), @userid, @inv_id, 'serial_no', OLD.serial_no);
  END IF;

  IF (OLD.qty <> NEW.qty) THEN
    INSERT INTO inventory_history VALUES (now(), @userid, @inv_id, 'qty', OLD.qty);
  END IF;

  IF (OLD.partid <> NEW.partid) THEN
    INSERT INTO inventory_history VALUES (now(), @userid, @inv_id, 'partid', OLD.partid);
  END IF;

  IF (OLD.last_sale <> NEW.last_sale) THEN
    INSERT INTO inventory_history VALUES (now(), @userid, @inv_id, 'last_sale', OLD.last_sale);
  END IF;
END
//
DELIMITER ;

同样,qty触发器有效,但last_sale没有。

2 个答案:

答案 0 :(得分:0)

我无法重现这个问题:

mysql> DROP TABLE IF EXISTS `inventory_history`, `inventory`;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE `inventory` (
    ->   `serial_no` varchar(255) DEFAULT NULL,
    ->   `qty` mediumint(9) DEFAULT NULL,
    ->   `partid` mediumint(9) unsigned DEFAULT NULL,
    ->   `last_sale` mediumint(9) unsigned DEFAULT NULL,
    ->   `date_created` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
    ->   `id` mediumint(9) unsigned NOT NULL AUTO_INCREMENT,
    ->   PRIMARY KEY (`id`),
    ->   KEY `partid` (`partid`),
    ->   KEY `date_created` (`date_created`),
    ->   KEY `last_sale` (`last_sale`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE `inventory_history` (
    ->   `date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    ->   `inventory_id` mediumint(9) unsigned NOT NULL,
    ->   `field_changed` enum('serial_no', 'qty', 'partid', 'last_sale', 'new') NOT NULL,
    ->   `changed_from` varchar(255) NOT NULL,
    ->   KEY `inventory_id` (`inventory_id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.01 sec)

mysql> DELIMITER //

mysql> CREATE TRIGGER `inv_new` AFTER INSERT ON `inventory`
    -> FOR EACH ROW
    -> BEGIN
    ->   DECLARE `_id` MEDIUMINT UNSIGNED DEFAULT NEW.`id`;
    ->   DECLARE `_date` TIMESTAMP DEFAULT NOW();
    ->   INSERT INTO `inventory_history` VALUES (`_date`, `_id`, 'new', 'new');
    -> END//
Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER ;

mysql> DROP TRIGGER IF EXISTS `inv_update`;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> DELIMITER //

mysql> CREATE TRIGGER `inv_update` AFTER UPDATE ON `inventory`
    -> FOR EACH ROW
    -> BEGIN
    ->   DECLARE `_inv_id` MEDIUMINT UNSIGNED DEFAULT OLD.`id`;
    -> 
    ->   IF (OLD.`serial_no` <> NEW.`serial_no`) THEN
    ->     INSERT INTO `inventory_history` VALUES (NOW(), `_inv_id`, 'serial_no', OLD.`serial_no`);
    ->   END IF;
    -> 
    ->   IF (OLD.`qty` <> NEW.`qty`) THEN
    ->     INSERT INTO `inventory_history` VALUES (NOW(), `_inv_id`, 'qty', OLD.`qty`);
    ->   END IF;
    -> 
    ->   IF (OLD.`partid` <> NEW.`partid`) THEN
    ->     INSERT INTO `inventory_history` VALUES (NOW(), `_inv_id`, 'partid', OLD.`partid`);
    ->   END IF;
    -> 
    ->   IF (OLD.`last_sale` <> NEW.`last_sale`) THEN
    ->     INSERT INTO `inventory_history` VALUES (NOW(), `_inv_id`, 'last_sale', OLD.`last_sale`);
    ->   END IF;
    -> END//
Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER ;

mysql> INSERT INTO `inventory`
    ->   (`serial_no`, `qty`, `partid`, `last_sale`)
    -> VALUES
    ->   ('1', 0, 0, 321);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT
    ->   `serial_no`,
    ->   `qty`,
    ->   `partid`,
    ->   `last_sale`,
    ->   `date_created`,
    ->   `id`
    -> FROM
    ->   `inventory`;
+-----------+------+--------+-----------+---------------------+----+
| serial_no | qty  | partid | last_sale | date_created        | id |
+-----------+------+--------+-----------+---------------------+----+
| 1         |    0 |      0 |       321 | 2016-11-15 00:00:51 |  1 |
+-----------+------+--------+-----------+---------------------+----+
1 row in set (0.00 sec)

mysql> SELECT
    ->   `date`,
    ->   `inventory_id`,
    ->   `field_changed`,
    ->   `changed_from`
    -> FROM
    ->   `inventory_history`;
+---------------------+--------------+---------------+--------------+
| date                | inventory_id | field_changed | changed_from |
+---------------------+--------------+---------------+--------------+
| 2016-11-15 00:00:51 |            1 | new           | new          |
+---------------------+--------------+---------------+--------------+
1 row in set (0.00 sec)

mysql> UPDATE `inventory`
    -> SET `last_sale` = 0, `qty` = 321
    -> WHERE `id` = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT
    ->   `date`,
    ->   `inventory_id`,
    ->   `field_changed`,
    ->   `changed_from`
    -> FROM
    ->   `inventory_history`;
+---------------------+--------------+---------------+--------------+
| date                | inventory_id | field_changed | changed_from |
+---------------------+--------------+---------------+--------------+
| 2016-11-15 00:00:51 |            1 | new           | new          |
| 2016-11-15 00:00:51 |            1 | qty           | 0            |
| 2016-11-15 00:00:51 |            1 | last_sale     | 321          |
+---------------------+--------------+---------------+--------------+
3 rows in set (0.00 sec)

mysql> SELECT
    ->   `serial_no`,
    ->   `qty`,
    ->   `partid`,
    ->   `last_sale`,
    ->   `date_created`,
    ->   `id`
    -> FROM
    ->   `inventory`;
+-----------+------+--------+-----------+---------------------+----+
| serial_no | qty  | partid | last_sale | date_created        | id |
+-----------+------+--------+-----------+---------------------+----+
| 1         |  321 |      0 |         0 | 2016-11-15 00:00:51 |  1 |
+-----------+------+--------+-----------+---------------------+----+
1 row in set (0.00 sec)

答案 1 :(得分:0)

几天后,事实证明这是一个简单而深刻的解释(不是总是这样吗?)。

我上面使用的触发器声明是:

  IF (OLD.last_sale <> NEW.last_sale) THEN
    INSERT INTO inventory_history VALUES (now(), @userid, @inv_id, 'last_sale', OLD.last_sale);
  END IF;

此声明的问题在于,它不会捕获从NULL0的值更改。它只捕获另一个值的值。所以我需要为NULL场景添加第二个语句:

  IF (OLD.last_sale IS NULL AND NEW.last_sale IS NOT NULL) THEN
    INSERT INTO inventory_history VALUES (now(), @userid, @inv_id, 'last_sale', OLD.last_sale);
  END IF;

全世界都是对的。 :)