如何在存储过程中使用事务?

Tha*_*you 0 mysql sql stored-procedures

我有一个 SQL 脚本,需要将其转换为参数化存储过程。我过去只编写过简单的函数,从未编写过带参数的复杂事务查询。

非常感谢任何帮助 - 下面简化了查询。该脚本实际上可以是包含交易和一些用户输入的任何内容。

-- transaction ensures i can clean up a mess, if one happens
begin;

-- parameters for the script; currently set manually before execution
set @parent_id := 123;
set @identifier := 'someid';

-- insert some row with user-specified values
insert into users (field1, field2) values (@parent_id, @identifier);

-- get the new id
set @user_id := last_insert_id();

-- do another insert
insert into usersmeta (user_id, field1, field2) values (@user_id, 1, 2);

-- if no errors happened yet, commit transaction
commit;

-- "return value"; some select query (could be 1 or many rows)
select users.id userid, usersmeta metaid
from users
join usersmeta on usersmeta.user_id = users.id;
Run Code Online (Sandbox Code Playgroud)

我一开始是这样的,但后来我几乎陷入了困境。我特别关心确保错误发生时,以某种方式对调用代码可见

delimiter ;; 
CREATE PROCEDURE mytask(IN parent_id INT(11), IN identifier VARCHAR(200)) 
BEGIN 
        SET @query = ??? 
        PREPARE q FROM @query; 
        EXECUTE q; 
        DEALLOCATE PREPARE q; 
END;; 
delimiter ; 
Run Code Online (Sandbox Code Playgroud)

Tha*_*you 5

经过大量的研究、试验和错误,但我认为我找到了一个非常好的解决方案。

DELIMITER //

CREATE PROCEDURE my_procedure (IN parent_id int, IN identifier varchar(255), OUT out_user_id int)
BEGIN

  -- local variable, set later after user is created
  DECLARE user_id int;

  -- rollback transaction and bubble up errors if something bad happens
  DECLARE exit handler FOR SQLEXCEPTION, SQLWARNING
  BEGIN
    ROLLBACK;
    RESIGNAL;
  END;

  START TRANSACTION;

  -- insert some row with user-specified values
  INSERT INTO users (field1, field2) values (parent_id, identifier);

  -- get the new id
  SET user_id = last_insert_id();

  -- do another insert
  INSERT INTO usersmeta (user_id, field1, field2) values (user_id, 1, 2);

  -- if no errors happened yet, commit transaction
  COMMIT;

  -- return
  SELECT user_id INTO out_user_id;

END //

DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

我可以这样使用它

-- run the procedure
CALL my_procedure(123, 'some_id', @user_id);

-- get the "return" value
SELECT @user_id as user_id;
Run Code Online (Sandbox Code Playgroud)

这绝对是我编写过的最复杂的存储过程。如果有人发现需要改进的地方,我很乐意了解如何改进。

  • 这里是龙:`INSERT INTO usersmeta (user_id, field1, field2)values (user_id, 1, 2);``VALUES()`中的`user_id`是不明确的,因为你有一个列名和一个程序变量一样的名字。不要这样做。当您想要另一个时,解析器可以假设您指的是一个,或者您最终可能会回收该列的未初始化的默认值。始终使程序变量与列名称不同。我的约定是变量前导下划线,例如“_user_id”。有关有问题的行为,请参阅此示例:https://dba.stackexchange.com/q/56957/11651 (2认同)