我在MySQL中有一个函数,如果它没有事务,则可以正常工作。当它转换为带有事务的过程时,它也可以工作。但是,当它是带有事务的函数时,MySQL会返回一个错误,指出函数或触发器中不允许提交。这种奇怪的行为有什么原因吗?
起作用的功能:
DELIMITER |
CREATE OR REPLACE FUNCTION TesteFunction() RETURNS INT DETERMINISTIC MODIFIES SQL DATA
BEGIN
DECLARE Retorno INT;
UPDATE Produto SET CD_Extra = "CD00472-5" WHERE CD_Codigo = 6;
SELECT ROW_COUNT() INTO Retorno;
RETURN Retorno;
END |
工作程序:
DELIMITER |
CREATE OR REPLACE PROCEDURE TesteFunction() DETERMINISTIC MODIFIES SQL DATA
BEGIN
START TRANSACTION READ WRITE;
UPDATE Produto SET CD_Extra = "CD00472-5" WHERE CD_Codigo = 6;
SELECT ROW_COUNT();
COMMIT;
END |
不起作用的功能:
DELIMITER |
CREATE OR REPLACE FUNCTION TesteFunction() RETURNS INT DETERMINISTIC MODIFIES SQL DATA
BEGIN
DECLARE Retorno INT;
START TRANSACTION READ WRITE;
UPDATE Produto SET CD_Extra = "CD00472-5" WHERE CD_Codigo = 6;
SELECT ROW_COUNT() INTO Retorno;
COMMIT;
RETURN Retorno;
END |
如果您创建带有事务控制语句的函数,则没有什么可以阻止客户端运行如下查询:
SELECT ... FROM MyTable WHERE TesteFunction() = 12345
这将为查询期间检查的每一行调用该函数一次。给定的查询是原子的,即它不能部分提交。
但是在查询执行期间提交事务是没有意义的。你不能这样做。
然而你不能以这种方式调用过程。您只能从 CALL 语句中调用过程,如下所示:
CALL TesteProc();
您不能在子查询或另一个 SQL 查询中的表达式中使用 CALL。它必须是一个独立的声明。所以它不会有在查询期间提交的相同问题。您可能会说,“但是如果我不在查询的 WHERE 子句中使用该函数怎么办?”
您可能不会,但任何其他客户将来都可以这样做。因此,实现一定不能允许这样的悖论发生,最安全的方法是在存储函数中禁止事务控制语句。
FWIW,出于同样的原因,您不能在触发器内使用事务控制。