简体   繁体   中英

Error Code: 1064 when creating MYSQL Trigger Statement

I'm trying to make a trigger that adds the value of input_qty to the value of shelf_qty , then sets input_qty = 0.

This is my attempt:

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;

I managed to get this to "work" by modifying Gordon's answer, but the trigger creates an infinite loop and doesn't update anything.

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

I would use something like this:

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

But the problem is that the assembled sql query on my server looks like this:

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

Update 2

The data for the query is collected from a form like this:

<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}...]

the form only submits a list of item/qty objects when a change was made. The qty field is the amount to increment the inventory by, not the actual qty amount.

As far as I know, operations after THEN aren't allowed and the only way I can think of to implement this with 1 query is with the trigger I attempted (which clearly doesn't work). Any suggestions would really be appreciated :)

Accepted Answer:

This worked for me:

 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')

That sounds really weird -- you have a column that will always be 0?. But if you want the set the value in the current row to 0 , then use a BEFORE UPDATE trigger:

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;

A TRIGGER cannot perform DML operations on tables that are referenced in the triggering statement. This restriction is documented in the MySQL Reference Manual.

To put that another way: the body of an UPDATE ON product trigger cannot issue an UPDATE statement against the product table.

This is one of the the things wrong with the trigger definition.

Beyond that, there's some syntax issues. The FOR EACH ROW should be followed by BEGIN keyword (the exception to that is a trigger that is a single statement.)

An IF statement should be closed with END IF (not just END )

But we have to re-think the whole approach here, not just fix the syntax.


Let's understand what we're trying to achieve, maybe by way of example.

Let's say we have table product

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

What would be the expected state of the table after we issue these statements:

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

That is, we can predict the outcome of these statements if no triggers are fired. But how are triggers supposed to influence the behavior, modify the results of these statements? What we are needing the trigger(s) to accomplish?

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

We can't write code to do something if we don't have a specification; we need to have some tests that we can use to verify that the code we write is doing what it's supposed to be doing. Otherwise, we're just flinging SQL syntax hoping that things will somehow work out.

What are we trying to accomplish?


If we are wanting to "increment" shelf_qty by some provided value, the normative pattern would be something like this (without any trigger):

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

We reference the current value of shelf_qty column, and add 1 to it, and then assign that new value back to the shelf_qty column.


Update 1

An expression can be used following the THEN keyword in a CASE expression. An addition operation in an expression is allowed.

The syntax shown for the "assembled sql query" (is valid; we'd hope that there's an ELSE qty before the END , in that it's a bit odd (not illegal, just unusual) to perform an UPDATE without a WHERE clause (to update every row in the table).

The syntax looks valid, but I can't verify the semantics, eg whether sku and qty are valid column references, et al.)

Personally, I'd do the UPDATE operation (added to the question) like this:

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

But I'm not exactly sure what we're supposed to be assigning to qty when sku isn't listed. My assumption is that we would leave the qty values on those rows unchanged. I'm just not understanding the benefit of a trigger for this use case,


Update 2

"As far as I know, operations after THEN aren't allowed" [in a CASE expression]

That depends what is meant by operations . Syntax for a CASE expression is:

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

or:

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

Where exprN are expressions. An addition operation can be used in an expression.

We could write the update like this:

 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')

But we would make it easier for a future reader to discern our intent by expressing it like this

 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')

Generalizing that is fairly straightforward. The SQL text generated by the application using named placeholders would be something like this:

 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 )

or using positional placeholders, like this:

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

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

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

We can see how the statement would be extended dynamically for a variable number of {sku:qty} combinations


followup

This all disrecommends using a TRIGGER. It's not the best way to handle the requirement. But, to answer the question that was asked...

If we have to use a trigger, given:

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

and

 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') 

then with this trigger defined:

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$$

The expected result would be:

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

But it doesn't make sense to me to do this with a trigger. I'm not seeing the benefit. It just seems to unnecessarily and confusingly modify the normal behavior of an UPDATE .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM