MySQL触发器调用过程时出错

时间:2011-11-08 13:51:16

标签: mysql sql database triggers

我有一个表(ft_ttd),想要按降序排序(num)并将评级数字插入评级栏。

Initial Table http://dl.dropbox.com/u/3922390/2.png

类似的东西:

Result Table http://dl.dropbox.com/u/3922390/1.png

我已经创建了一个程序。

CREATE PROCEDURE proc_ft_ttd_sort  
BEGIN   

CREATE TEMPORARY TABLE ft_ttd_sort
(id int (2),  
num int (3),  
rating int (2) AUTO_INCREMENT PRIMARY KEY);    

INSERT INTO ft_ttd_sort (id, num)   SELECT id, num FROM ft_ttd ORDER BY num DESC;    
TRUNCATE TABLE ft_ttd;   
INSERT INTO ft_ttd SELECT * FROM ft_ttd_sort;  
DROP TABLE ft_ttd_sort;   
END;

当我打电话时 - 效果很好。

CALL proc_ft_ttd_sort;

之后我创建了触发器调用此过程。

CREATE TRIGGER au_ft_ttd_fer AFTER UPDATE ON ft_ttd FOR EACH ROW 
BEGIN
CALL proc_ft_ttd_sort(); 
END;

现在每次更新ft_ttd表时都会出错。

UPDATE ft_ttd SET num = 9 WHERE id = 3;
ERROR 1422 (HY000): Explicit or implicit commit is not allowed in stored function ortrigger.

任何想法如何使它工作?也许这个过程可以优化? 谢谢!

2 个答案:

答案 0 :(得分:2)

create table语句是一个隐式提交,因为它是DDL。基本上,答案是您无法在触发器中创建表。

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

答案 1 :(得分:1)

触发器无法执行此操作

除了DDL之外,基于触发器的方法有一些困难。首先,您要修改已更新的表,即not permitted in MySQL 5

其次,你真的想要一个语句级触发器,而不是FOR EACH ROW - 不需要为每个受影响的行重新排列整个表 - 但那是not supported in MySQL 5

动态计算"rating"

那么......只使用MySQL ROW_NUMBER() workaround动态计算rating吗?

-- ALTER TABLE ft_ttd DROP COLUMN rating; -- if you like

    SELECT id,
           num,
           @i := @i + 1 AS rating
      FROM ft_ttd
CROSS JOIN (SELECT @i := 0 AS zero) d
  ORDER BY num DESC;

不幸的是,您无法将该SELECT包装在VIEW中(因为视图的"SELECT statement cannot refer to system or user variables")。但是,您可以在可选择的存储过程中隐藏它:

 CREATE PROCEDURE sp_ranked_ft_ttd () BEGIN
     SELECT id, num, @i := @i + 1 AS rating
       FROM ft_ttd CROSS JOIN (SELECT @i := 0 AS zero) d
   ORDER BY num DESC
 END

或者如果必须

则更新

作为一个kluge,如果您必须在表中存储rating而不是计算它,您可以根据需要运行此UPDATE:

    UPDATE t
CROSS JOIN (    SELECT id, @i := @i + 1 AS new_rating
                  FROM ft_ttd
            CROSS JOIN (SELECT @i := 0 AS zero) d
              ORDER BY num DESC
           ) ranked
        ON ft_ttd.id = ranked.id SET ft_ttd.rating = ranked.new_rating;

现在指示您的客户端代码忽略rating IS NULL - 那些尚未排名的行。更好的是,为你创建一个VIEW。

进一步说,您可以通过CREATE EVENT定期更新。