MySQL: Transaktion innerhalb einer gespeicherten Prozedur

69

Die Grundstruktur meiner gespeicherten Prozedur ist:

BEGIN

    .. Declare statements ..

    START TRANSACTION;

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

    COMMIT;

END

MySQL-Version: 5.1.61-0ubuntu0.11.10.1-log

Wenn 'Abfrage 2' fehlschlägt, wird derzeit das Ergebnis von 'Abfrage 1' festgeschrieben.

  • Wie kann ich die Transaktion zurücksetzen, wenn eine der Abfragen fehlschlägt?
Priyank Kapasi
quelle
4
Beachten Sie auch, dass es eine Denkschule mit Leuten gibt, die glauben, dass Transaktionen außerhalb des Bereichs einer gespeicherten Prozedur aufgerufen werden sollten und dass Prozeduren / Funktionen in der Lage sein sollten, jede aufrufende Transaktion vollständig einzuschließen.
Jé Queue

Antworten:

60

Schauen Sie sich http://dev.mysql.com/doc/refman/5.0/en/declare-handler.html an

Grundsätzlich deklarieren Sie den Fehlerhandler, der den Rollback aufruft

START TRANSACTION;

DECLARE EXIT HANDLER FOR SQLEXCEPTION 
    BEGIN
        ROLLBACK;
        EXIT PROCEDURE;
    END;
COMMIT;
rkosegi
quelle
4
DECLARE EXIT HANDLER FÜR NICHT GEFUNDENES ROLLBACK;
Priyank Kapasi
6
DECLARE EXIT HANDLER FÜR SQLWARNING ROLLBACK;
Priyank Kapasi
5
DECLARE EXIT HANDLER FÜR SQLEXCEPTION ROLLBACK;
Priyank Kapasi
5
@Priyank Kapasi, großartig, nur um zu beachten, ich denke, nur der letzte wird wirklich benötigt.
Rkosegi
3
DECLARE sollte nur nach einer BEGIN-Anweisung verwendet werden
CME64
35

Nur eine Alternative zum Code von 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
Priyank Kapasi
quelle
7

Hier ist ein Beispiel für eine Transaktion, die bei einem Fehler zurückgesetzt wird und den Fehlercode zurückgibt.

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 ;

Dies setzt voraus, dass Autocommit auf 0 gesetzt ist. Ich hoffe, dies hilft.

user2288580
quelle
2

Dies ist nur eine Erklärung, die in anderen Antworten nicht angesprochen wird

Zumindest in neueren Versionen von MySQL wird Ihre erste Abfrage nicht festgeschrieben .

Wenn Sie es in derselben Sitzung abfragen, werden die Änderungen angezeigt. Wenn Sie es jedoch in einer anderen Sitzung abfragen, sind die Änderungen nicht vorhanden und werden nicht festgeschrieben .

Was ist los?

Wenn Sie eine Transaktion öffnen und eine darin enthaltene Abfrage fehlschlägt, bleibt die Transaktion geöffnet und die Änderungen werden weder festgeschrieben noch rückgängig gemacht .

So ACHTUNG , jede Tabelle / Zeile , die mit einer früheren Abfrage wie gesperrt wurde SELECT ... FOR SHARE/UPDATE, UPDATE, INSERToder jede andere Sperrabfrage gesperrt hält , bis diese Sitzung getötet wird (und führt einen Rollback), oder bis eine nachfolgende Abfrage verpflichtet explizit ( COMMIT) oder implizit , wodurch die Teiländerungen dauerhaft werden (was Stunden später passieren kann, während sich die Transaktion in einem Wartezustand befindet).

Aus diesem Grund besteht die Lösung darin, Handler sofort zu deklarieren, ROLLBACKwenn ein Fehler auftritt.

Extra

Im Handler können Sie den Fehler auch mit erneut auslösen RESIGNAL, andernfalls wird die gespeicherte Prozedur "Erfolgreich" ausgeführt.

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

    START TRANSACTION;
        #.. Query 1 ..
        #.. Query 2 ..
        #.. Query 3 ..
    COMMIT;
END
Madacol
quelle