【问题标题】:MySQL : transaction within a stored procedureMySQL:存储过程中的事务
【发布时间】:2012-04-15 23:44:25
【问题描述】:

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

BEGIN

    .. Declare statements ..

    START TRANSACTION;

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

    COMMIT;

END

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

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

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

【问题讨论】:

  • 另请注意,有一种观点认为事务应该在存储过程的范围之外调用,并且过程/函数应该能够完全包含任何调用事务。

标签: mysql sql stored-procedures transactions


【解决方案1】:

看看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;

【讨论】:

  • 声明未找到回滚的退出处理程序;
  • 声明 SQLWARNING ROLLBACK 的退出处理程序;
  • 声明 SQLEXCEPTION ROLLBACK 的退出处理程序;
  • @Priyank Kapasi,太好了,请注意,我认为只有最后一个是真正需要的。
  • DECLARE 只能在 BEGIN 语句之后使用
【解决方案2】:

只是 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

【讨论】:

    【解决方案3】:

    这里是一个在错误时回滚并返回错误代码的事务示例。

    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。 希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      [这只是其他答案中未涉及的解释]

      至少在最新版本的 MySQL 中,您的第一个查询没有提交

      如果您在同一个会话中查询它,您将看到更改,但如果您从不同会话中查询它,更改不存在,它们没有提交

      发生了什么事?

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

      所以小心,任何被先前查询(如 SELECT ... FOR SHARE/UPDATEUPDATEINSERT 或任何其他锁定查询)锁定的表/行都会保持锁定状态,直到该会话结束杀死(并执行回滚),或者直到后续查询明确提交它(COMMIT)或implicitly,从而使部分更改永久(可能会在几小时后发生,而事务处于等待状态)。

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


      额外

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

      BEGIN
          DECLARE EXIT HANDLER FOR SQLEXCEPTION 
              BEGIN
                  ROLLBACK;
                  RESIGNAL;
              END;
      
          START TRANSACTION;
              #.. Query 1 ..
              #.. Query 2 ..
              #.. Query 3 ..
          COMMIT;
      END
      

      【讨论】:

        猜你喜欢
        • 2021-06-02
        • 1970-01-01
        • 1970-01-01
        • 2020-07-08
        • 1970-01-01
        • 2013-09-02
        • 1970-01-01
        相关资源
        最近更新 更多