MySQL 事务难题

发布于 2024-10-09 14:53:30 字数 343 浏览 5 评论 0原文

我需要在单个原子事务中执行多次插入。例如:

开始交易;

插入...

插入...

提交;

然而,当 MySQL 遇到错误时,它只会中止导致错误的特定语句。例如,如果第二个插入语句中有错误,提交仍然会发生,并且第一个插入语句将被记录。因此,当发生错误时,MySQL 事务并不是真正的事务。为了解决这个问题,我使用了一个错误退出处理程序来回滚事务。现在交易已悄然中止,但我不知道问题出在哪里。

所以你面临的难题是:

如何让 MySQL 在遇到错误时中止事务,并将错误代码传递给调用者?

I need to perform several inserts in a single atomic transaction. For example:

start transaction;

insert ...

insert ...

commit;

However when MySQL encounters an error it aborts only the particular statement that caused the error. For example, if there is an error in the second insert statement the commit will still take place and the first insert statement will be recorded. Thus, when errors occur a MySQL transaction is not really a transaction. To overcome this problem I have used an error exit handler where I rollback the transaction. Now the transaction is silently aborted but I don't know what was the problem.

So here is the conundrum for you:

How can I both make MySQL abort a transaction when it encounters an error, and pass the error code on to the caller?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

叶落知秋 2024-10-16 14:53:30

如何让 MySQL 在遇到错误时中止事务,并将错误代码传递给调用者?

MySQL 确实会将错误代码传递给调用者,并且根据此错误代码,调用者可以自由决定是否要提交目前完成的工作(忽略此特定 INSERT< 的错误) /code> 语句)或回滚事务。

这与 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;

任一插入失败都将回滚所做的所有更改按该程序并退出。

How can I both make MySQL abort a transaction when it encounters an error, and pass the error code on to the caller?

MySQL does pass error code to the caller and based on this error code the caller is free to decide whether it wants to commit work done up to the moment (ignoring the error with this particular INSERT statement) or to rollback the transaction.

This is unlike PostgreSQL which always aborts the transaction on error and this behavior is a source of many problems.

Update:

It's a bad practice to use an unconditional ROLLBACK inside the stored procedures.

Stored procedures are stackable and transactions are not, so a ROLLBACK within a nested stored procedure will roll back to the very beginning of the transaction, not to the state of the stored procedure execution.

If you want to use transactions to restore the database state on errors, use SAVEPOINT constructs and DECLARE HANDLER to rollback to the savepoints:

CREATE PROCEDURE prc_work()
BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK TO sp_prc_work;
        SAVEPOINT sp_prc_work;
        INSERT  …;
        INSERT  …;
        …
END;

Failure in either insert will roll back all changes made by the procedure and exit it.

独闯女儿国 2024-10-16 14:53:30

使用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 段,它也不会显示详细的错误消息,但是嘿,它可以工作。

Using Mr. Quassnoi's example, here's my best approach to catching specific errors:

The idea is to use a tvariable to catch a simple error message, then you can catch sql states you think may happen to save custom messages to your variable:

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 ;

Then you just do your usual call, and do a select statement for the variable like:

call prc_work(); select @prc_work_error;

This will return either NULL if no error, or the message error in case of an error. If you need persistent error message you can optionally create a table to store it.

It's tedious and not very flexible because requires a DECLARE EXIT HANDLER segment for each status code you want to catch, it won't also show detailed error messages but hey, it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文