基于 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 叫做 products
和 category
。
products
table 有 prod_grp
和 prod_code
列。
category
table 有 cat_type
和 cat_code
列。
item
table 的 item_code
是通过连接 item
的 item_id
、cat_code
和 prod_code
列派生的]、category
和 products
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;
我使用 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 叫做 products
和 category
。
products
table 有 prod_grp
和 prod_code
列。
category
table 有 cat_type
和 cat_code
列。
item
table 的 item_code
是通过连接 item
的 item_id
、cat_code
和 prod_code
列派生的]、category
和 products
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;