【问题标题】:Error Code: 1064 when creating MYSQL Trigger Statement错误代码:创建 MYSQL 触发器语句时出现 1064
【发布时间】:2019-02-13 16:11:19
【问题描述】:

我正在尝试创建一个触发器,将input_qty 的值与shelf_qty 的值相加,然后设置input_qty = 0。

这是我的尝试:

DELIMITER $$
CREATE TRIGGER inventory_update
AFTER UPDATE ON `products`
FOR EACH ROW
IF OLD.`input_qty` > 0 THEN
BEGIN
    DECLARE new_shelf_qty INT(11);
    SET new_shelf_qty := OLD.`input_qty` + OLD.`shelf_qty`;
    UPDATE `products` SET `input_qty` = 0, `shelf_qty` = new_shelf_qty;

END $$
DELIMITER;

我设法通过修改 Gordon 的答案使其“工作”,但触发器会创建一个无限循环并且不会更新任何内容。

DELIMITER $$
CREATE TRIGGER inventory_update
BEFORE UPDATE ON `products`
FOR EACH ROW
BEGIN
    DECLARE new_shelf_qty INT(11);
    IF OLD.`input_qty` > 0 THEN
        SET new_shelf_qty = OLD.input_qty + OLD.shelf_qty;
        SET new.input_qty = 0;
    END IF;
END $$

更新

我会使用这样的东西:

UPDATE product t
SET t.shelf_qty = t.shelf_qty + 1 
WHERE t.id = 1 ;

但问题是我的服务器上组装好的sql查询是这样的:

UPDATE `products`
SET `qty` = CASE
    WHEN `sku` = 'foo' THEN `qty` + qty1
    WHEN `sku` = 'bar' THEN `qty` + qty2
    ...
END;

更新 2

查询的数据是从这样的表格中收集的:

<input id="sku1" type="number">
<input id="sku2" type="number">
<input id="sku3" type="number">
...
<input type="submit" value="Save"> 

print(POST body) //[{sku1:qty1}, {sku2:qty2}, {sku3:qty3}...]

表单仅在进行更改时提交项目/数量对象的列表。数量字段是增加库存的数量,而不是实际数量。

据我所知,THEN 之后的操作是不允许的,我能想到的用 1 个查询来实现这一点的唯一方法是使用我尝试的触发器(这显然不起作用)。任何建议都将不胜感激:)

接受的答案:

这对我有用:

 UPDATE products t
    SET t.qty = t.qty 
              + CASE t.sku 
                WHEN 'foo' THEN 1
                WHEN 'bar' THEN 2
                ELSE 0
                END
  WHERE t.sku IN ('foo','bar')

【问题讨论】:

  • CASE 表达式中的THEN 关键字后面可以使用表达式(允许在表达式中进行加法运算)。 “组装的 sql 查询”显示的语法是有效的,我们希望在 END 之前有一个 ELSE qty... 在没有 WHERE 子句的情况下执行 UPDATE 有点奇怪(更新表中的每一行)。 (我们无法验证语义,例如 skuqty 是否是有效的列引用。)
  • UPDATE product t SET t.qty = t.qty + CASE t.sku WHEN 'fee' THEN 1 WHEN 'fi' THEN 2 ELSE 0 END WHERE ... 就我个人而言,我会做同样的事情,但我不确定我们应该在什么时候分配给qty sku 未列出...我的假设是我们将保持这些行上的 qty 值不变。我只是不了解触发器对这个用例的好处,

标签: mysql sql triggers


【解决方案1】:

TRIGGER 无法对触发语句中引用的表执行 DML 操作。此限制记录在 MySQL 参考手册中。

换句话说:UPDATE ON product 触发器的主体不能针对product 表发出UPDATE 语句。

这是触发器定义的问题之一。

除此之外,还有一些语法问题。 FOR EACH ROW 后面应该跟 BEGIN 关键字(例外情况是触发器是单个语句。)

IF 语句应以END IF 结束(不仅仅是END

但我们必须在这里重新考虑整个方法,而不仅仅是修正语法。


让我们了解一下我们想要实现的目标,也许是通过示例的方式。

假设我们有桌子product

 id  mfr  input_qty  shelf_qty
 --  ---  ---------  ---------   
  1  fee         3         39
  2  fi          0          7

我们发出这些语句后,表的预期状态是什么:

 UPDATE product SET mfr = 'fo'  WHERE id = 1 ;
 UPDATE product SET input_qty = 4 WHERE id = 2 ;

也就是说,如果没有触发触发器,我们可以预测这些语句的结果。但是触发器应该如何影响行为,修改这些语句的结果呢?我们需要触发器来完成什么?

 UPDATE product SET input_qty = 5 , shelf_quantity = 11 WHERE id = 1;

如果我们没有规范,我们就无法编写代码来做某事;我们需要进行一些测试来验证我们编写的代码是否在做它应该做的事情。否则,我们只是在抛出 SQL 语法,希望事情能以某种方式解决。

我们想要完成什么?


如果我们想通过某个提供的值“增加”shelf_qty,规范模式将是这样的(没有任何触发器):

 UPDATE product t
    SET t.shelf_qty = t.shelf_qty + 1 
  WHERE t.id = 1 ;

我们引用shelf_qty 列的当前值,并将其加1,然后将该新值分配回shelf_qty 列。


更新 1

可以在CASE 表达式中的THEN 关键字之后使用表达式。允许在表达式中进行加法运算。

“组装 sql 查询”显示的语法(有效;我们希望在 END 之前有一个 ELSE 数量,因为执行 @987654340 有点奇怪(不是非法的,只是不寻常) @ 没有 WHERE 子句(更新表中的每一行)。

语法看起来有效,但我无法验证语义,例如skuqty 是否是有效的列引用等)

就个人而言,我会像这样执行 UPDATE 操作(添加到问题中):

UPDATE product t
   SET t.qty = t.qty 
             + CASE t.sku 
                 WHEN 'fee' THEN 1 
                 WHEN 'fi' THEN 2 
                 ELSE 0
               END

但是当sku 未列出时,我不确定我们应该为qty 分配什么。我的假设是我们将保持这些行上的qty 值不变。我只是不了解触发器对这个用例的好处,


更新 2

“据我所知,THEN 之后的操作是不允许的” [在 CASE 表达式中]

这取决于操作的含义。 CASE 表达式的语法是:

 CASE WHEN expr1 THEN expr2 WHEN expr3 THEN expr4 ... ELSE expr5 END

或:

 CASE expr1 WHEN expr2 THEN expr3 WHEN expr4 THEN expr5 ... ELSE expr6 END

exprN 是表达式。表达式中可以使用加法运算。

我们可以这样写更新:

 UPDATE products t
    SET t.qty = CASE
                WHEN t.sku = 'foo' THEN t.qty + 1
                WHEN t.sku = 'bar' THEN t.qty + 2
                ELSE t.qty
                END
  WHERE t.sku IN ('foo','bar')

但我们会通过这样的表达方式让未来的读者更容易辨别我们的意图

 UPDATE products t
    SET t.qty = t.qty 
              + CASE t.sku 
                WHEN 'foo' THEN 1
                WHEN 'bar' THEN 2
                ELSE 0
                END
  WHERE t.sku IN ('foo','bar')

概括起来相当简单。应用程序使用命名占位符生成的 SQL 文本如下所示:

 UPDATE products t
    SET t.qty = t.qty 
              + CASE t.sku 

                WHEN  :sku1  THEN  :qty1
                WHEN  :sku2  THEN  :qty2
                WHEN  :sku3  THEN  :qty3

                ELSE 0
                END
  WHERE t.sku IN ( :wsku1 , :wsku2 , :wsku3 )

或使用位置占位符,如下所示:

 UPDATE products t
    SET t.qty = t.qty 
              + CASE t.sku 

                WHEN  ?  THEN  ?
                WHEN  ?  THEN  ?
                WHEN  ?  THEN  ?

                ELSE 0
                END
  WHERE t.sku IN ( ? , ? , ? )

我们可以看到如何为可变数量的{sku:qty} 组合动态扩展语句


跟进

不建议使用 TRIGGER。这不是处理需求的最佳方式。但是,要回答所提出的问题...

如果我们必须使用触发器,给定:

 product 
 id  sku  input_qty   shelf_qty
 --  ---  ---------   ---------
  3  fo           0          41
  4  fum          0          11

 UPDATE product t
    SET t.input_qty = CASE t.sku 
                      WHEN  'fo'   THEN 1 
                      WHEN  'fum'  THEN 2
                      ELSE 0
                      END
  WHERE t.sku IN ('fo','fum') 

然后定义这个触发器:

DELIMITER $$

CREATE TRIGGER product_bu
BEFORE UPDATE ON product
FOR EACH ROW
BEGIN
   IF NEW.input_qty > 0 THEN
      -- add provided value of input_qty to shelf_qty
      SET NEW.shelf_qty = HEW.shelf_qty + NEW.input_qty;
      -- set input_qty to zero
      SET NEW.input_qty = 0;
   END IF;
END$$

预期的结果是:

 product 
 id  sku  input_qty   shelf_qty
 --  ---  ---------   ---------
  3  fo           0          42
  4  fum          0          13

但是对我来说用触发器来做这件事没有意义。我没有看到好处。它似乎只是不必要且令人困惑地修改了UPDATE 的正常行为。

【讨论】:

  • 感谢这个答案很有帮助!我更新了我的答案,以明确我为什么需要这个触发器。
【解决方案2】:

这听起来很奇怪——你有一个永远是 0 的列?但是如果你想将当前行中的值设置为0,那么使用BEFORE UPDATE触发器:

DELIMITER $$
CREATE TRIGGER inventory_update
BEFORE UPDATE ON `products`
FOR EACH ROW
BEGIN
    IF OLD.`input_qty` > 0 THEN
        DECLARE new_shelf_qty INT(11);
        SET new_shelf_qty = OLD.input_qty + OLD.shelf_qty;
        SET new.input_qty = 0;
    END IF;
END $$
DELIMITER;

【讨论】:

  • 仍然遇到同样的错误。它在第 11 行显示“附近”,即END;
  • 我也在使用input_qty 行,因为我不知道增加shelf_qty 值的更好方法。
猜你喜欢
  • 1970-01-01
  • 2011-06-27
  • 2015-10-01
  • 2021-04-15
  • 1970-01-01
  • 2015-04-08
  • 2021-11-25
  • 2023-03-26
  • 2021-12-15
相关资源
最近更新 更多