从触发器调用存储过程


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可以解决此问题。
Mark D

Answers:


24

有一个很好的理由说明为什么您永远不要从触发器内部调用存储过程。

触发器本质上是存储过程。他们的行动实际上很难退缩。即使所有基础表都是InnoDB,您也会遇到一定比例的共享行锁和排他行锁的烦人的间歇性。如果触发器正在处理表而INSERT和UPDATE被停滞以在每次对触发器的调用内执行重型MVCC,则将是这种情况。

不要忘记触发器需要开销。实际上,根据MySQL存储过程编程,在第256页的“触发器开销”标题下显示以下内容:

重要的是要记住,触发器必须在其所应用的DML语句上增加开销。实际的开销量将取决于触发器的性质,但是---由于所有MySQL触发器都执行FOR EACH ROW ---对于处理大量行的语句,开销会迅速累积。因此,您应该避免在触发器中放置任何昂贵的SQL语句或过程代码。

触发开销的扩展说明在第529-531页中给出。该部分的结论指出:

这里的教训是:由于触发器代码将对受DML语句影响的每一行执行一次,因此触发器很容易成为DML性能的最重要因素。触发器主体中的代码必须尽可能轻巧,尤其是-触发器中的任何SQL语句都应尽可能地由索引支持。

我在之前的文章中解释了触发器的其他令人讨厌的方面。

摘要

强烈建议不要从Trigger调用任何存储过程,即使MySQL允许它也是如此。您应该检查一下MySQL 5.5的当前限制


有趣,感谢大家的注意。我们的环境中缺少事务查询可以缓解事务问题。但是,我很欣赏累积开销的想法。我想我将看数据库一段时间,以查看此更改的结果。
Mark D

我认为将触发器与存储过程合并是不准确的。至少,在存储过程中启动和提交事务是有效的。如果您尝试在触发器中执行相同的操作,则MySQL会抱怨。这很愚蠢,因为拥有一个触发器,该触发器需要响应某些更改而以事务方式更新一个或多个表是一个完全有效的用例,应以简单的方式予以支持。
aroth 2014年

所以我有这个触发因素,那确实很大。它在插入和更新时对我的表执行一些计算。当触发器很复杂时,MySQL中的触发器确实会变得很痛苦。将触发器分解为过程会容易得多。
Lamar

8

事实证明,这是困扰我几个小时的问题,信不信由你。

我可以轻松定义一个名为sp_set-comment_count的过程。但是,当调用上述过程时,它的工作方式不同。

CALL sp_set-comment_count(我只能假设这是因为服务器将-解释为减号)。

此后,我将存储过程名称更改为仅使用下划线,并且似乎已解决了所有问题。


聚会晚了,但是:您已经使用带引号的标识符创建了SP,并且在名称中允许使用特殊字符,因此您应该在其他地方类似地引用它: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;
$$

谢谢,这实际上使我思考正确的方向。实际上,我的sp被称为sp-set_comment_count。当被触发器调用时,似乎出现的问题是,从触发器调用SP时-不断抛出错误。
Mark D

1

看起来逗号后面AC是语法错误:

UPDATE usergroups
   SET allCount = AC,
 WHERE ........

有效点,但不是错误的实际原因,在这种情况下,我只是从该查询中修剪了一些额外的集合,而忘了删除了,
Mark D
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.