使用 MySQL 8.0 触发器自动生成内部产品编号
Posted
技术标签:
【中文标题】使用 MySQL 8.0 触发器自动生成内部产品编号【英文标题】:Automate Internal Product Number Generation with MySQL 8.0 Triggers 【发布时间】:2021-07-04 05:31:59 【问题描述】:我对 SQL 的模式方面不是很有经验,我必须创建一个系统来在将新行输入数据库时生成内部产品编号。我决定使用 SQL 触发器是最好的方法,而不是外部编程,因为它在 SQL 内部看起来更简洁和可行。
内部产品编号示例:101-001
内部产品编号有两部分,我分别调用group_id
和product_id
。 group_id
与 category_id
相关(它是如何存储在数据库中的),并且我正在使用一个表来连接它们,PartCategory
。 product_id
在每个 group_id
中都是递增的,但是如果删除了一行,它们应该会继续,就像没有删除一样,我认为 MAX 是合适的。
这是我正在使用的两个表的示例:
DROP TABLE IF EXISTS Part;
CREATE TABLE Part (
id INT AUTO_INCREMENT PRIMARY KEY,
category_id INT,
internalPartNumber VARCHAR(255),
name VARCHAR(255),
createdate DATETIME,
group_id INT,
product_id INT
);
DROP TABLE IF EXISTS PartCategory;
CREATE TABLE PartCategory (
id INT AUTO_INCREMENT PRIMARY KEY,
category_id INT,
name VARCHAR(255),
internal_id INT,
last_product_id INT
);
INSERT INTO PartCategory (internal_id, category_id, name) VALUE (101, 2, 'category1');
INSERT INTO PartCategory (internal_id, category_id, name) VALUE (102, 3, 'category2');
INSERT INTO Part (category_id, name, createdate, group_id, product_id) VALUE (2, 'screw', NOW(), 101, 1);
INSERT INTO Part (category_id, name, createdate, group_id, product_id) VALUE (2, 'spanner', NOW(), 101, 2);
INSERT INTO Part (category_id, name, createdate, group_id, product_id) VALUE (3, 'hammer', NOW(), 102, 1);
INSERT INTO Part (category_id, name, createdate, group_id, product_id) VALUE (3, 'paint', NOW(), 102, 2);
DELIMITER //
CREATE TRIGGER add_group_id BEFORE INSERT ON Part
FOR EACH ROW
BEGIN
UPDATE Part
LEFT JOIN PartCategory
ON Part.category_id = PartCategory.category_id
SET NEW.group_id = PartCategory.internal_id
WHERE Part.group_id IS NULL;
UPDATE Part
SET NEW.product_id = MAX(Part.product_id) OVER(PARTITION BY Part.category_id ORDER BY createdate)
WHERE NEW.product_id IS NULL;
SET NEW.internalPartNumber = CONCAT(RIGHT(NEW.group_id+1000, 3),'-',RIGHT(NEW.product_id+1000, 3))
WHERE NEW.internalPartNumber IS NULL;
END; //
DELIMITER ;
INSERT INTO Part (category_id, name, createdate) VALUE (2, 'bolt', NOW());
INSERT INTO Part (category_id, name, createdate) VALUE (3, 'metal', NOW());
SELECT internalPartNumber, name FROM Part
ORDER BY internalPartNumber;
但是在触发器内部,我不能使用表 Part,因为它调用了触发器。我不知道如何做到这一点。前六个插入代表通过脚本输入数据库的数据,这些我可以设置任何行,最后两个代表将由用户输入的数据(触发器需要处理的数据)。
从这个例子我想看看:
internalPartNumber | name
101-001 | screw
101-002 | spanner
101-003 | bolt
102-001 | hammer
102-002 | paint
102-003 | metal
【问题讨论】:
【参考方案1】:CREATE TRIGGER tr_bi_Part
BEFORE INSERT
ON Part
FOR EACH ROW
SET NEW.internalPartNumber = CONCAT( ( SELECT internal_id
FROM PartCategory
WHERE category_id = NEW.category_id ),
'-',
( SELECT LPAD(1 + COALESCE(COUNT(*), 0), 3, '0')
FROM Part
WHERE category_id = NEW.category_id ) );
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=58fd1bc356c0a2f771d8160235be3c0c
【讨论】:
@harrygdt 考虑到代码不检查:类别存在,内部编号过大。 是的,我明白了。在我的情况下,这不应该是一个问题,但我会为下一个人在数据库中使用它做一个注释。 这有一个竞争条件。插入的两个并发会话将看到相同的COUNT(*)
,因此分配相同的内部部件号。以上是关于使用 MySQL 8.0 触发器自动生成内部产品编号的主要内容,如果未能解决你的问题,请参考以下文章
Mysql 8.0 OCP认证考试原题题库整理-第4题(CUUG内部资料)