MySQL -无法更新存储函数/触发器中的表,因为它已被调用此存储函数/触发器的语句使用(select)

xoshrz7s  于 2022-11-21  发布在  Mysql
关注(0)|答案(3)|浏览(397)

我是新的MySQL和学习触发器。我有2个表,我想要:当已经插入表(detail_transaction)时,另一个表(item)“stock”字段改变。

  • “项目”表

| 标识符|姓名|标价|股票|
| - -|- -|- -|- -|
| 一个|物料_A|十五|九百|
| 2个|物料_B|九个|五百个|

  • 'detail_transaction'表

| 标识符|id_项目|计数器|总价|
| - -|- -|- -|- -|
| 一个|一个|五个|七十五|
如果在“detail_transaction”表中插入新行,我希望“item”表中具有相同“id”的“stock”字段减少并调整为“detail_transaction”的“count”。例如:我在“detail_transaction”表中插入新行:
| 标识符|id_项目|计数器|总价|
| - -|- -|- -|- -|
| 2个|一个|| 一百五十个|
我希望将“item”表更新为:
| 标识符|姓名|标价|股票|
| - -|- -|- -|- -|
| 一个|物料_A|十五|八百九十|
| 2个|物料_B|九个|五百个|
我创建了一个触发器来尝试实现我的目的,但是当我尝试在'detail_transaction'中插入新行时,我得到了以下错误:* 无法更新存储函数/触发器中'item'表,因为它已被调用此存储函数/触发器语句使用 *
我的触发器:

DELIMITER $$
CREATE TRIGGER update_stock
AFTER INSERT
    ON detail_transaction 
    FOR EACH ROW
BEGIN
    UPDATE item
    JOIN detail_transaction ON detail_transaction.id_item = item.id
    SET stock = stock - NEW.count
    WHERE item.id = NEW.id_item;
END$$
DELIMITER ;

然后,我在detail_transaction表中插入了一行:

INSERT INTO detail_transaction (id, id_item, count, total_price)
VALUES (2, 1, 10, (SELECT price FROM item WHERE item.ID = 1) * 10);

但是我得到了这个错误。我该怎么做才能解决这个问题呢?是不是因为我尝试INSERT时的SELECT部分?谢谢你的回答。

mum43rcc

mum43rcc1#

首先(且固执己见):触发器很难调试、测试和维护。2包含触发器的系统 * 真的 * 很难调试,因为它们引入了副作用-“我在这个表上做了X,然后Y发生在另一个表上”。3作为一个开发人员,你必须把所有的触发器都记在脑子里,以了解单个语句可能会做什么。
以您的示例为例,您可能在Item中的“stock”字段上有一个触发器,用于创建一个采购订单记录,以便在库存福尔斯阈值时补充库存。purchase order表可能有一个插入触发器,用于在accounts payable中创建一个记录。该触发器链实现了有效的业务逻辑,其中但是当插入detail_transaction的操作突然被拒绝,因为产品供应商超出了他们的付款限额时,会导致非常复杂的调试过程。
触发器面临的一个挑战是,数据库引擎不希望发生无限循环,也不希望您正在选择的字段的值由于触发器的激发而发生更改。
此外,您不需要该联接-您可以从NEW中获取值。

DELIMITER $$
CREATE TRIGGER update_stock
AFTER INSERT
    ON detail_transaction 
    FOR EACH ROW
BEGIN
    UPDATE item
    SET stock = stock - NEW.count
    WHERE item.id = NEW.id_item;
END$$
DELIMITER ;

方法是使用变量:

SET @PRICE = ((SELECT price FROM item WHERE item.ID = 1) * 10);

INSERT INTO detail_transaction (id, id_item, count, total_price)
VALUES (2, 1, 10, @PRICE);

SELECT * from item;

请参阅fiddle

  • EDIT* -其他一些答案显示了一个更简单的解决方案-计算触发器中的总价。

理智的人对如何使用触发器意见不一--但我会建议使用触发器来计算派生值--“给定物品的总库存”或“交易的总价”--通常是个坏主意。你实际上是在复制数据--物品的总库存水平既是交易的总和,* 又是 * 行中的属性。总价既是“价格 * 数量”,* 和 * 一行中的属性。如果有人对total_price或total_stock执行update语句(有意或作为bug的一部分)会发生什么?哪个值是正确的?

6bc51xsx

6bc51xsx2#

您不应混用insert..values和insert..select我会将插入重写为

INSERT INTO detail_transaction (id, id_item, count, total_price)
 select 2, 1, 10,  price * 10 
 FROM item 
WHERE item.ID = 1;

尽管我的选择是插入前触发器

DELIMITER $$
CREATE TRIGGER update_stock before INSERT  ON detail_transaction 
FOR EACH ROW
BEGIN
    set new.total_price = (
     select item.price * new.count 
     FROM item 
     WHERE item.ID = new.id
     );
END$$
DELIMITER ;

带有插入件

INSERT INTO detail_transaction (id, id_item, count, total_price)
VALUES (2, 1, 10, null);

您的after insert发布失败,因为您使用了多表更新来调用触发触发器的表,这是不允许的,此问题的解决方案见上一个答案。

qaxu7uf2

qaxu7uf23#

CREATE TABLE item (
  `id` INTEGER AUTO_INCREMENT PRIMARY KEY,
  `name` VARCHAR(255),
  `price` INTEGER,
  `stock` INTEGER
);
INSERT INTO item VALUES
  ('1', 'Item_A', '15', '900'),
  ('2', 'Item_B', '9', '500');
SELECT * FROM item;

CREATE TABLE detail_transaction (
  `id` INTEGER AUTO_INCREMENT PRIMARY KEY,
  `id_item` INTEGER,
  `count` INTEGER,
  `total_price` INTEGER,
  FOREIGN KEY (`id_item`) REFERENCES `item` (`id`)
);
INSERT INTO detail_transaction VALUES
  ('1', '1', '5', '75');
SELECT * FROM detail_transaction;

| 标识符|姓名|标价|股票|
| - -|- -|- -|- -|
| 一个|物料_A|十五|九百|
| 2个|物料_B|九个|五百个|
| 标识符|id_项目|计数器|总价|
| - -|- -|- -|- -|
| 一个|一个|五个|七十五|
第一个
| 标识符|id_项目|计数器|总价|
| - -|- -|- -|- -|
| 一个|一个|五个|七十五|
| 2个|一个|10个|一百五十个|
| 标识符|姓名|标价|股票|
| - -|- -|- -|- -|
| 一个|物料_A|十五|八百九十|
| 2个|物料_B|九个|五百个|
fiddle
每个触发器只包含一个语句,所以不需要BEGIN-END和к DELIMITER命令。

相关问题