MySQL:存储过程中的事务


69

我的存储过程的基本结构是

BEGIN

    .. Declare statements ..

    START TRANSACTION;

        .. Query 1 ..
        .. Query 2 ..
        .. Query 3 ..

    COMMIT;

END

MySQL版本: 5.1.61-0ubuntu0.11.10.1-log

当前,如果“查询2”失败,则提交“查询1”的结果。

  • 如果任何查询失败,如何回滚事务?

4
还要注意,人们之间存在着流派,他们认为应该在存储过程的范围之外调用事务,并且过程/功能应该能够完全包含任何调用事务。
JE队列

Answers:


60

看看http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html

基本上,您声明错误处理程序,它将调用回滚

START TRANSACTION;

DECLARE EXIT HANDLER FOR SQLEXCEPTION 
    BEGIN
        ROLLBACK;
        EXIT PROCEDURE;
    END;
COMMIT;

4
DECLARE退出处理程序,未找到回滚;
Priyank Kapasi

6
用于SQLWARNING回滚的DECLARE退出处理程序;
Priyank Kapasi

5
用于SQLEXCEPTION回滚的DECLARE退出处理程序;
Priyank Kapasi'4

5
@Priyank Kapasi,太好了,只是要注意,我认为只需要最后一个。
rkosegi'4

3
只能在BEGIN语句之后使用DECLARE
CME64 '18

35

只是rkosegi的代码的替代方案,

BEGIN

    .. Declare statements ..

    DECLARE EXIT HANDLER FOR SQLEXCEPTION 
    BEGIN
          .. set any flags etc  eg. SET @flag = 0; ..
          ROLLBACK;
    END;

    START TRANSACTION;

        .. Query 1 ..
        .. Query 2 ..
        .. Query 3 ..

    COMMIT;
    .. eg. SET @flag = 1; ..

END

7

这是一个事务示例,它将在错误时回滚并返回错误代码。

DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `SP_CREATE_SERVER_USER`(
    IN P_server_id VARCHAR(100),
    IN P_db_user_pw_creds VARCHAR(32),
    IN p_premium_status_name VARCHAR(100),
    IN P_premium_status_limit INT,
    IN P_user_tag VARCHAR(255),
    IN P_first_name VARCHAR(50),
    IN P_last_name VARCHAR(50)
)
BEGIN

    DECLARE errno INT;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
    GET CURRENT DIAGNOSTICS CONDITION 1 errno = MYSQL_ERRNO;
    SELECT errno AS MYSQL_ERROR;
    ROLLBACK;
    END;

    START TRANSACTION;

    INSERT INTO server_users(server_id, db_user_pw_creds, premium_status_name, premium_status_limit)
    VALUES(P_server_id, P_db_user_pw_creds, P_premium_status_name, P_premium_status_limit);

    INSERT INTO client_users(user_id, server_id, user_tag, first_name, last_name, lat, lng)
    VALUES(P_server_id, P_server_id, P_user_tag, P_first_name, P_last_name, 0, 0);

    COMMIT WORK;

END$$
DELIMITER ;

假设自动提交设置为0。希望这会有所帮助。


2

这只是其他答案未解决的解释

至少在最新版本的Mysql中,您的第一个查询未提交

如果在同一会话下查询它,则将看到更改,但是,如果从其他会话中查询它,则更改不存在,因此不会提交

这是怎么回事?

当您打开一个事务,并且其中的查询失败时,该事务将保持打开状态,不提交也不回滚更改。

所以要小心,任何表/一行被锁定,就像以前的查询SELECT ... FOR SHARE/UPDATEUPDATEINSERT或任何其他锁定查询,一直锁定,直到该会话被杀死(并执行回滚),或直到随后的查询提交它明确(COMMIT)或隐式,从而使部分更改永久生效(可能在几个小时后发生,而事务处于等待状态)。

这就是为什么解决方案涉及ROLLBACK在发生错误时立即声明处理程序的原因。

额外

在处理程序内部,您还可以使用引发错误RESIGNAL,否则存储过程将“成功”执行

BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION 
        BEGIN
            ROLLBACK;
            RESIGNAL;
        END;

    START TRANSACTION;
        #.. Query 1 ..
        #.. Query 2 ..
        #.. Query 3 ..
    COMMIT;
END
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.