我需要在单个原子事务中执行几个插入。例如:
启动交易;
插入...
插入...
提交;
但是,当MySQL遇到错误时,它只会中止导致错误的特定语句。例如,如果第二个insert语句中存在错误,则仍将发生提交,并将记录第一个insert语句。因此,当发生错误时,MySQL事务实际上不是事务。为了解决这个问题,我使用了一个错误退出处理程序来回滚事务。现在该事务被静默中止,但我不知道是什么问题。
所以这是你的难题:
如何让MySQL在遇到错误时中止事务,并将错误代码传递给调用者?
答案 0 :(得分:3)
我如何让
MySQL
在遇到错误时中止事务,并将错误代码传递给调用者?
MySQL
确实将错误代码传递给调用者,并且根据此错误代码,调用者可以自由决定是否要提交当前完成的工作(忽略此特定INSERT
的错误声明)或回滚交易。
这与PostgreSQL
不同,ROLLBACK
总是在出错时中止事务,这种行为是许多问题的根源。
<强>更新强>
在存储过程中使用无条件ROLLBACK
是一种不好的做法。
存储过程是可堆叠的,事务不是,因此嵌套存储过程中的SAVEPOINT
将回滚到事务的最开始,而不是存储过程执行的状态。
如果要使用事务来恢复错误时的数据库状态,请使用DECLARE HANDLER
构造和CREATE PROCEDURE prc_work()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK TO sp_prc_work;
SAVEPOINT sp_prc_work;
INSERT …;
INSERT …;
…
END;
回滚到保存点:
{{1}}
任一插入失败都将回滚该过程所做的所有更改并退出。
答案 1 :(得分:2)
使用Quassnoi先生的例子,这是我捕捉特定错误的最佳方法:
这个想法是使用tvariable来捕获一个简单的错误消息,然后你可以捕获你认为可能发生的自定义消息保存到变量的sql状态:
DELIMITER $$
DROP PROCEDURE IF EXISTS prc_work $$
CREATE PROCEDURE prc_work ()
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '23000'
BEGIN
SET @prc_work_error = 'Repeated key';
ROLLBACK TO sp_prc_work;
END;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SET @prc_work_error = 'Unknown error';
ROLLBACK TO sp_prc_work;
END;
START TRANSACTION;
SAVEPOINT sp_prc_work;
INSERT into test (id, name) VALUES (1, 'SomeText');
COMMIT;
END $$
DELIMITER ;
然后你只需要进行常规调用,并对变量执行select语句,如:
call prc_work(); select @prc_work_error;
如果没有错误,则返回NULL,如果出错,则返回消息错误。如果您需要持久性错误消息,您可以选择创建一个表来存储它。
这很乏味且不够灵活,因为要为每个要捕获的状态代码需要一个DECLARE EXIT HANDLER段,它也不会显示详细的错误消息,但是嘿,它可以工作。