插入时触发;代替A做B

Trigger on insert; instead of A do B

我有两个表:warehouseinventory:

CREATE TABLE warehouse (
    bin VARCHAR(6) NOT NULL AUTO_INCREMENT,
    qty INT
    PRIMARY KEY(bin)
);

CREATE TABLE inventory (
    item INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(10),
    PRIMARY KEY(item)
);

我想设置一个触发器,以便 inventory 上的任何插入,如果该插入没有唯一名称,也将其添加到 warehouse,否则适当增加 warehouse.qty .

如果我要在 inventory 中插入一行:

INSERT INTO inventory 
VALUES('', 'hammer");

 inventory         warehouse
----------------------------
item | name        bin | qty
----------------------------
 1    hammer        1     1

已添加 inventory 中的一行,但 warehouse 中也添加了一行。然后添加另一个唯一行:

INSERT INTO inventory 
VALUES('', 'pliers");

 inventory         warehouse
----------------------------
item | name        bin | qty
----------------------------
 1    hammer        1     1
 2    pliers        2     1

终于添加了第二个锤子。

INSERT INTO inventory 
VALUES('', 'hammer");

 inventory         warehouse
----------------------------
item | name        bin | qty
----------------------------
 1    hammer        1     2
 2    pliers        2     1

请注意,当第二次 'hammer' 插入发生时,没有向 inventory 添加新行,而是 warehouse 中的相关行增加了 qty

如何在 SQL 中创建此触发器?

14.1.18 CREATE TABLE Syntax

...

  • Some attributes do not apply to all data types. AUTO_INCREMENT applies only to integer and floating-point types. DEFAULT does not apply to the BLOB, TEXT, GEOMETRY, and JSON types.

...

CREATE TABLE warehouse (
    bin VARCHAR(6) NOT NULL AUTO_INCREMENT, -- <- It's not possible
    qty INT,
    PRIMARY KEY(bin)
);

你可能有一些选择,有一些考虑:

  • 如果item列必须严格按照顺序(AUTO_INCREMENT),可以在存储过程中封装INSERT逻辑:

示例Rextester

mysql> DROP PROCEDURE IF EXISTS `insert_inventory`;
Query OK, 0 rows affected (0.00 sec)

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

mysql> CREATE TABLE `warehouse` (
    ->     `bin` VARCHAR(6) NOT NULL,
    ->     `qty` INT DEFAULT 1,
    ->     PRIMARY KEY(`bin`)
    -> );
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE `inventory` (
    ->     `item` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->     `name` VARCHAR(10),
    ->     PRIMARY KEY(`item`),
    ->     UNIQUE KEY(`name`)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TRIGGER `trg_ai_inventory` AFTER INSERT ON `inventory`
    -> FOR EACH ROW
    ->     INSERT INTO `warehouse` (`bin`)
    ->     VALUES (NEW.`item`);
Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER //

mysql> CREATE PROCEDURE `insert_inventory`(`_name` VARCHAR(10))
    -> BEGIN
    ->     DECLARE `_item` INT DEFAULT (SELECT `item`
    ->                                  FROM `inventory`
    ->                                  WHERE `name` = `_name`);
    ->     IF `_item` IS NULL THEN
    ->         INSERT INTO `inventory` (`name`)
    ->         VALUES (`_name`);
    ->     ELSE
    ->         UPDATE `warehouse`
    ->         SET `qty` = `qty` + 1
    ->         WHERE `bin` = `_item`;
    ->     END IF;
    -> END//
Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER ;

mysql> CALL `insert_inventory`('hammer');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT `item`, `name`
    -> FROM `inventory`;
+------+--------+
| item | name   |
+------+--------+
|    1 | hammer |
+------+--------+
1 row in set (0.00 sec)

mysql> SELECT `bin`, `qty`
    -> FROM `warehouse`;
+-----+------+
| bin | qty  |
+-----+------+
| 1   |    1 |
+-----+------+
1 row in set (0.00 sec)

mysql> CALL `insert_inventory`('hammer');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT `item`, `name`
    -> FROM `inventory`;
+------+--------+
| item | name   |
+------+--------+
|    1 | hammer |
+------+--------+
1 row in set (0.00 sec)

mysql> SELECT `bin`, `qty`
    -> FROM `warehouse`;
+-----+------+
| bin | qty  |
+-----+------+
| 1   |    2 |
+-----+------+
1 row in set (0.00 sec)

mysql> CALL `insert_inventory`('pliers');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT `item`, `name`
    -> FROM `inventory`;
+------+--------+
| item | name   |
+------+--------+
|    1 | hammer |
|    2 | pliers |
+------+--------+
2 rows in set (0.00 sec)

mysql> SELECT `bin`, `qty`
    -> FROM `warehouse`;
+-----+------+
| bin | qty  |
+-----+------+
| 1   |    2 |
| 2   |    1 |
+-----+------+
2 rows in set (0.00 sec)

忽略(示例 Rextester):

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

mysql> CREATE TABLE `warehouse` (
    ->     `bin` VARCHAR(6) NOT NULL,
    ->     `qty` INT DEFAULT 1,
    ->     PRIMARY KEY(`bin`)
    -> );
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE `inventory` (
    ->     `item` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->     `name` VARCHAR(10),
    ->     PRIMARY KEY(`item`),
    ->     UNIQUE KEY(`name`)
    -> );
Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER //

mysql> CREATE TRIGGER `trg_bi_inventory` BEFORE INSERT ON `inventory`
    -> FOR EACH ROW
    -> BEGIN
    ->     DECLARE `_item` INT DEFAULT (SELECT `item`
    ->                                  FROM `inventory`
    ->                                  WHERE `name` = NEW.`name`);
    ->     IF `_item` IS NOT NULL THEN
    ->         UPDATE `warehouse`
    ->         SET `qty` = `qty` + 1
    ->         WHERE `bin` = `_item`;
    ->     END IF;
    -> END//
Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER ;

mysql> CREATE TRIGGER `trg_ai_inventory` AFTER INSERT ON `inventory`
    -> FOR EACH ROW
    ->     INSERT INTO `warehouse` (`bin`)
    ->     VALUES (NEW.`item`);
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT IGNORE `inventory` (`name`)
    -> VALUES ('hammer');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT `item`, `name`
    -> FROM `inventory`;
+------+--------+
| item | name   |
+------+--------+
|    1 | hammer |
+------+--------+
1 row in set (0.00 sec)

mysql> SELECT `bin`, `qty`
    -> FROM `warehouse`;
+-----+------+
| bin | qty  |
+-----+------+
| 1   |    1 |
+-----+------+
1 row in set (0.00 sec)

mysql> INSERT IGNORE `inventory` (`name`)
    -> VALUES ('hammer');
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT `item`, `name`
    -> FROM `inventory`;
+------+--------+
| item | name   |
+------+--------+
|    1 | hammer |
+------+--------+
1 row in set (0.00 sec)

mysql> SELECT `bin`, `qty`
    -> FROM `warehouse`;
+-----+------+
| bin | qty  |
+-----+------+
| 1   |    2 |
+-----+------+
1 row in set (0.00 sec)

mysql> INSERT IGNORE `inventory` (`name`)
    -> VALUES ('pliers');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT `item`, `name`
    -> FROM `inventory`;
+------+--------+
| item | name   |
+------+--------+
|    1 | hammer |
|    3 | pliers |
+------+--------+
2 rows in set (0.00 sec)

mysql> SELECT `bin`, `qty`
    -> FROM `warehouse`;
+-----+------+
| bin | qty  |
+-----+------+
| 1   |    2 |
| 3   |    1 |
+-----+------+
2 rows in set (0.00 sec)

关于重复密钥更新(示例 Rextester):

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

mysql> CREATE TABLE `warehouse` (
    ->     `bin` VARCHAR(6) NOT NULL,
    ->     `qty` INT DEFAULT 1,
    ->     PRIMARY KEY(`bin`)
    -> );
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE `inventory` (
    ->     `item` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->     `name` VARCHAR(10),
    ->     `count` INT DEFAULT 1,
    ->     PRIMARY KEY(`item`),
    ->     UNIQUE KEY(`name`)
    -> );
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TRIGGER `trg_ai_inventory` AFTER INSERT ON `inventory`
    -> FOR EACH ROW
    ->     INSERT INTO `warehouse` (`bin`)
    ->     VALUES (NEW.`item`);
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TRIGGER `trg_au_inventory` AFTER UPDATE ON `inventory`
    -> FOR EACH ROW
    ->     UPDATE `warehouse`
    ->     SET `qty` = `qty` + 1
    ->     WHERE `bin` = OLD.`item`;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO `inventory` (`name`)
    -> VALUES ('hammer')
    ->     ON DUPLICATE KEY UPDATE `count` = `count` + 1;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT `item`, `name`
    -> FROM `inventory`;
+------+--------+
| item | name   |
+------+--------+
|    1 | hammer |
+------+--------+
1 row in set (0.00 sec)

mysql> SELECT `bin`, `qty`
    -> FROM `warehouse`;
+-----+------+
| bin | qty  |
+-----+------+
| 1   |    1 |
+-----+------+
1 row in set (0.00 sec)

mysql> INSERT INTO `inventory` (`name`)
    -> VALUES ('hammer')
    ->     ON DUPLICATE KEY UPDATE `count` = `count` + 1;
Query OK, 2 rows affected (0.00 sec)

mysql> SELECT `item`, `name`
    -> FROM `inventory`;
+------+--------+
| item | name   |
+------+--------+
|    1 | hammer |
+------+--------+
1 row in set (0.00 sec)

mysql> SELECT `bin`, `qty`
    -> FROM `warehouse`;
+-----+------+
| bin | qty  |
+-----+------+
| 1   |    2 |
+-----+------+
1 row in set (0.00 sec)

mysql> INSERT INTO `inventory` (`name`)
    -> VALUES ('pliers')
    ->     ON DUPLICATE KEY UPDATE `count` = `count` + 1;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT `item`, `name`
    -> FROM `inventory`;
+------+--------+
| item | name   |
+------+--------+
|    1 | hammer |
|    3 | pliers |
+------+--------+
2 rows in set (0.00 sec)

mysql> SELECT `bin`, `qty`
    -> FROM `warehouse`;
+-----+------+
| bin | qty  |
+-----+------+
| 1   |    2 |
| 3   |    1 |
+-----+------+
2 rows in set (0.00 sec)