为什么MySQL中的函数不允许事务提交?

问题描述 投票:0回答:1

我在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 |
mysql transactions commit stored-functions
1个回答
1
投票

如果您创建带有事务控制语句的函数,则没有什么可以阻止客户端运行如下查询:

SELECT ... FROM MyTable WHERE TesteFunction() = 12345

这将为查询期间检查的每一行调用该函数一次。给定的查询是原子的,即它不能部分提交。

但是在查询执行期间提交事务是没有意义的。你不能这样做。

然而你不能以这种方式调用过程。您只能从 CALL 语句中调用过程,如下所示:

CALL TesteProc();
您不能在子查询或另一个 SQL 查询中的表达式中使用 CALL。它必须是一个独立的声明。所以它不会有在查询期间提交的相同问题。

您可能会说,“但是如果我不在查询的 WHERE 子句中使用该函数怎么办?”

您可能不会,但任何其他客户将来都可以这样做。因此,实现一定不能允许这样的悖论发生,最安全的方法是在存储函数中禁止事务控制语句。

FWIW,出于同样的原因,您不能在触发器内使用事务控制。

© www.soinside.com 2019 - 2024. All rights reserved.