基于 AUTO_INCREMENT 字段的触发器在多行 INSERT 上工作不正确

Trigger based on AUTO_INCREMENT field works incorrectly on multiple row INSERT

我使用 MySQL 5.6 和 item table (InnoDB),如下所示。

Name Type
item_id int(11)
item_code varchar(20)
name varchar(100)
cat_type varchar(50)
prod_grp varchar(12)
stock int(11)
price double
date_added timestamp

item_id 列是一个 AUTO_INCREMENT 字段。

还有另外两个 table 叫做 productscategoryproducts table 有 prod_grpprod_code 列。 category table 有 cat_typecat_code 列。

item table 的

item_code 是通过连接 itemitem_idcat_codeprod_code 列派生的]、categoryproducts table。

我为 item table 定义了一个触发器,它可以在 INSERT 变为 item 时自动更新 item_code table.

DELIMITER $$

CREATE TRIGGER `add_item_code` 
  BEFORE INSERT ON `item`
   FOR EACH ROW 
   BEGIN
    IF (NEW.item_code IS NULL OR NEW.item_code = '' ) THEN
       SET NEW.item_code = ( 
            SELECT CONCAT("MM-", cat_code, "-", prod_code, "-", id.item_id)
            FROM products, category, 
                (SELECT AUTO_INCREMENT AS item_id
                FROM  INFORMATION_SCHEMA.TABLES
                WHERE TABLE_SCHEMA = 'comdb'
                AND TABLE_NAME = 'item') AS id
            WHERE products.prod_grp = NEW.prod_grp
              AND category.cat_type = NEW.cat_type
       );
    END IF;
   END; $$
DELIMITER ;

当我执行如下插入时,触发器工作并按预期更新 item_code。

INSERT INTO `item`
(`prod_grp`, `name`, `stock`, `price`, `cat_type`)
VALUES ('h & k','Crooks mirror','10','333.5','home')

比如说,如果最后一个 item_id 是 723,那么 item_code 会更新为“MM-hm-hk-724”,因为 724 是下一个 AUTO_INCREMENT 值。

但是,当我使用多个值进行插入时,AUTO_INCREMENT 值变为最新值,这导致 item_code.

不正确
INSERT INTO `item`
(`prod_grp`, `name`, `stock`, `price`, `cat_type`)
VALUES ('h & k','Crooks mirror','10','333.5','home'), 
('DIY','Screw Driver','14','10.5','tools'), 
('h & k','bulb','120','21.5','electricals');

比如说,如果最后一个 item_id 是 723,那么 item_code 将针对这三个项目更新如下

item_id item_code
724 MM-hm-hk-724
725 MM-tl-dy-727
726 MM-el-hk-727

如您所见,最后两个 item_code 更新为错误的 item_id。 我根本不明白发生了什么。有人可以帮助解决这个问题吗?

技巧 - 您可以尝试使用用户定义的变量。

CREATE TRIGGER `add_item_code` 
BEFORE INSERT ON `item`
FOR EACH ROW 
BEGIN
    IF (NEW.item_code IS NULL OR NEW.item_code = '' ) THEN
        IF @add_item_code_autoincrement IS NULL THEN
            SELECT AUTO_INCREMENT 
            INTO @add_item_code_autoincrement
            FROM  INFORMATION_SCHEMA.TABLES
            WHERE TABLE_SCHEMA = DATABASE()
            AND TABLE_NAME = 'item';
        END IF;
        SET NEW.item_code = CONCAT("MM-", @add_item_code_autoincrement);
        SET @add_item_code_autoincrement = @add_item_code_autoincrement + 1;
    END IF;
END;

https://dbfiddle.uk/?rdbms=mysql_5.6&fiddle=97a725cd6a3850d52e6548ac1f09778c

这只是一个示例,因此从其他表中检索值的操作已被删除。变量名一定要长而复杂,以免造成干扰。

警告! 此代码在并发插入中绝对不安全。如果执行并发插入,则此代码很可能会产生错误值。如果您使用 INSERT .. ON DUPLICATE KEY UPDATE,它也会给出错误的结果。

此外,即使在最有利的条件下,此代码也可能会给出错误的结果。因此,如果您仍然决定使用此技巧,请创建服务事件过程,该过程将检查生成值的正确性并在生成错误时更正它们。例如,可以每分钟执行一次,检查不超过2分钟的行。


additional table will be an overkill. But I am curious to know the solution. – Die-Bugger

示意图:

CREATE additional_table (id INT AUTO_INCREMENT PRIMARY KEY) 
    AUTO_INCREMENT = {current AUTO_INCREMENT value for `item` table} ;

CREATE TRIGGER `add_item_code` 
BEFORE INSERT ON `item`
FOR EACH ROW 
BEGIN
    INSERT INTO additional_table VALUES (DEFAULT);
    SET @add_item_code_autoincrement := LAST_INSERT_ID();
--  DELETE FROM additional_table WHERE id < @add_item_code_autoincrement;
    IF (NEW.item_code IS NULL OR NEW.item_code = '' ) THEN
        SET NEW.item_code = CONCAT("MM-", @add_item_code_autoincrement);
    END IF;
END;