Код ошибки: 1064 при создании оператора триггера MYSQL - PullRequest
0 голосов
/ 13 февраля 2019

Я пытаюсь создать триггер, который добавляет значение 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;

Мне удалось заставить это "работать", изменив ответ Гордона, но триггер создает бесконечный цикл и ничего не обновляет.

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

Обновление

Iбудет использовать что-то вроде этого:

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

, форма предоставляет список объектов item / qty только после внесения изменений.Поле qty - это сумма, равная приросту инвентаря, а не фактическому количеству.

Насколько я знаю, операции после 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')

Ответы [ 2 ]

0 голосов
/ 13 февраля 2019

TRIGGER не может выполнять операции DML над таблицами, на которые есть ссылки в операторе запуска.Это ограничение описано в справочном руководстве по MySQL.

Другими словами: тело триггера UPDATE ON product не может выдать оператор UPDATE для таблицы product.

Это одна из ошибок в определении триггера.

Помимо этого, есть некоторые проблемы с синтаксисом.За FOR EACH ROW должно следовать ключевое слово BEGIN (исключение составляет триггер, являющийся одним оператором.)

Оператор IF должен быть закрыт с END IF (не просто * 1016).*)

Но мы должны переосмыслить весь подход, а не просто исправить синтаксис.


Давайте разберемся, чего мы пытаемся достичь, может быть, в качестве примера.

Допустим, у нас есть таблица 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

Выражение можно использовать после ключевого слова THEN в выражении CASE.Допускается операция добавления в выражении.

Синтаксис, показанный для «собранного запроса sql» (действителен; мы надеемся, что перед END стоит ELSE qty, поскольку он немного странный(не незаконно, просто необычно) выполнить UPDATE без предложения WHERE (чтобы обновить каждую строку в таблице).

Синтаксис выглядит корректным, но я не могу проверить семантику, например* являются ли sku и qty действительными ссылками на столбцы и т. д.)

Лично я бы сделал операцию UPDATE (добавлена ​​к вопросу) следующим образом:

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

Но я не совсем уверен, что мы должны назначить qty, когда sku не указан.Я предполагаю, что мы оставим значения 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.

0 голосов
/ 13 февраля 2019

Это звучит очень странно - у вас есть столбец, который всегда будет 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...