Вызов хранимой процедуры из триггера

17

Я создал хранимую процедуру в MySQL, используя следующий синтаксис.

DROP PROCEDURE IF EXISTS `sp-set_comment_count`;

DELIMITER $$

CREATE PROCEDURE `sp_set-comment_count` (IN _id INT)
BEGIN
   -- AC   - AllCount
   DECLARE AC INT DEFAULT 0;

   SELECT COUNT(*) AS ac
     INTO AC
     FROM usergroups AS ug
LEFT JOIN usergroup_comments AS ugm ON ugm.`gid` = ug.`id`
LEFT JOIN mediagallery AS dm ON ugm.mid = dm.`id`
    WHERE dm.`status` NOT IN (200, 201, 202, 203, 204, 205)
      AND ug.`id` = _id;

   UPDATE usergroups
      SET allCount = AC,
    WHERE usergroups.`id` = _id;

END $$
DELIMITER ;

К вашему сведению, я значительно упростила хранимую процедуру, но знаю, что она работает без проблем.

Я хотел бы иметь возможность настроить триггер из usergroup_comments, который работает следующим образом.

DROP TRIGGER IF EXISTS `usergroups_comments_insert` 

CREATE TRIGGER `usergroups_comments_insert` AFTER INSERT ON `usergroups_comment`
    FOR EACH ROW
    BEGIN
       CALL sp-set-comment_count(NEW.`gid`);
    END;

Но по какой-то причине каждый раз, когда я делаю это, mysql выдает ошибку, которая не очень полезна, так как в строке 4 есть синтаксическая ошибка.

Я просмотрел документацию по mysql и нашел некоторую информацию об ограничениях триггеров, но нашел ее довольно запутанной.

http://dev.mysql.com/doc/refman/5.1/en/stored-program-restrictions.html

Любые идеи будут полезны.

Марк Д
источник
Таким образом, оказывается, что проблема с вызванной выше хранимой процедурой заключалась в том, что в ее имени был дефис. Изменение имени хранимой процедуры на sp_set_comment_count решило проблему.
Марк Д

Ответы:

24

Есть веская причина, по которой вы никогда не должны вызывать хранимые процедуры изнутри триггеров.

Триггеры по своей природе являются хранимыми процедурами. Их действия практически трудно откатить . Даже если все базовые таблицы являются InnoDB, вы будете испытывать пропорциональный объем общих блокировок строк и раздражающую прерывистость от исключительных блокировок строк. Так было бы, если бы триггеры манипулировали таблицами со стагнациями INSERT и UPDATE для выполнения MVCC в тяжелых условиях внутри каждого вызова триггера .

Не забывайте, что триггеры требуют накладных расходов. На самом деле, согласно программированию хранимых процедур MySQL , страница 256 под заголовком «Служебная нагрузка триггера» говорит следующее:

Важно помнить, что по необходимости триггеры увеличивают накладные расходы в операторе DML, к которому они применяются. фактическое количество служебных данных будет зависеть от природы триггера, но, поскольку все триггеры MySQL выполняются FOR EACH ROW, накладные расходы могут быстро накапливаться для операторов, обрабатывающих большое количество строк. Поэтому вы должны избегать размещения каких-либо дорогих операторов SQL или процедурного кода в триггерах.

Подробное объяснение накладных расходов триггера приведено на страницах 529-531. Заключительный пункт из этого раздела гласит следующее:

Урок здесь заключается в следующем: поскольку код триггера будет выполняться один раз для каждой строки, на которую воздействует инструкция DML, триггер может легко стать наиболее значимым фактором производительности DML. Код внутри тела триггера должен быть как можно более легким, и, в частности, любые операторы SQL в триггере должны поддерживаться индексами, когда это возможно.

Я объяснил другие неприятные аспекты триггеров в предыдущем посте.

РЕЗЮМЕ

Я настоятельно рекомендую не вызывать хранимые процедуры из Trigger , даже если MySQL это позволяет. Вам следует проверить текущие ограничения для MySQL 5.5 .

RolandoMySQLDBA
источник
Интересно, спасибо за головы. Отсутствие транзакционных запросов в нашей среде смягчает проблему транзакции. Однако я могу оценить идею накопления накладных расходов. Я предполагаю, что некоторое время буду наблюдать за БД, чтобы увидеть, каков результат этого изменения.
Марк Д
Я не думаю, что правильно связывать триггеры с хранимыми процедурами. Как минимум, допустимо начать и зафиксировать транзакцию в хранимой процедуре. MySQL жалуется, если вы пытаетесь сделать то же самое в триггере. Что глупо, потому что наличие триггера, который требует транзакционного обновления одной или нескольких таблиц в ответ на какое-то изменение, является полностью допустимым вариантом использования, который должен поддерживаться простым способом.
aroth
Итак, у меня есть этот триггер, который действительно большой. Он выполняет несколько расчетов на моей таблице, как при вставке, так и при обновлении. Триггеры в Mysql могут действительно стать болезненными, когда они сложны. Было бы намного проще разбить курок на процедуры.
Ламар
8

Вот и получается, что это проблема, которая мучила меня несколько часов, хотите верьте, хотите нет.

Я могу легко определить процедуру с именем sp_set-comment_count. Однако при вызове указанной процедуры она не работает так же.

CALL sp_set-comment_count (я могу только предположить, что это потому, что сервер интерпретирует - как минус).

С тех пор я изменил имя хранимой процедуры, чтобы использовать только подчеркивания, и, похоже, все решено.

Марк Д
источник
Позднее к партии , но: вы создали СП с помощью цитируемый идентификатор, который позволил специальные символы в имени, так что вы должны ссылаться на него так же в других местах:CALL `sp-set-comment_count`(NEW.`gid`);
mustaccio
5

Если это говорит о синтаксической ошибке, скорее всего вы забыли изменить разделитель (как вы сделали для хранимой процедуры). Так вам нужно

DELIMITER $$
CREATE TRIGGER `usergroups_comments_insert` AFTER INSERT ON `usergroups_comment`
FOR EACH ROW
BEGIN
   CALL sp_set_count(NEW.`gid`);
END;
$$
a1ex07
источник
Спасибо, это заставило меня задуматься о правильном пути. На самом деле мой sp назывался sp-set_comment_count. При вызове триггером кажется, что проблема заключалась в том, что при вызове SP из триггера - продолжал выдавать ошибку.
Марк Д
1

Похоже, запятая после ACсинтаксической ошибки:

UPDATE usergroups
   SET allCount = AC,
 WHERE ........
user22800
источник
Действительная точка, но не фактическая причина ошибки, в этом случае я просто обрезал некоторые дополнительные наборы из этого запроса и забыл удалить,
Марк Д