2010-12-28 7 views
4

Ich muss mehrere Einsätze in einer einzigen atomaren Transaktion durchführen. Zum Beispiel:MySQL-Transaktion Rätsel

Transaktion starten;

Einsatz ...

Einsatz ...

begehen;

Wenn MySQL jedoch einen Fehler feststellt, wird nur die bestimmte Anweisung abgebrochen, die den Fehler verursacht hat. Wenn beispielsweise in der zweiten insert-Anweisung ein Fehler auftritt, wird die Festschreibung weiterhin ausgeführt und die erste insert-Anweisung wird aufgezeichnet. Wenn also Fehler auftreten, ist eine MySQL-Transaktion nicht wirklich eine Transaktion. Um dieses Problem zu beheben, habe ich einen Fehlerbeendungshandler verwendet, bei dem ich die Transaktion zurücksetze. Jetzt wird die Transaktion stillgelegt, aber ich weiß nicht, was das Problem war.

So, hier ist das Rätsel für Sie:

Wie kann ich beide machen MySQL eine Transaktion abbrechen, wenn ein Fehler auftritt, und den Fehlercode weitergeben an den Anrufer?

+0

Verwenden Sie einen Tabellentyp, der Transaktionen unterstützt (InnoDB)? MyISAM -Tabellen unterstützen keine Transaktionen (daher sind START, COMMIT und ROLLBACK keine Op-Anweisungen), was zu dem von Ihnen beschriebenen Verhalten führen kann. – Piskvor

+0

Ja, ich verwende InnoDB. –

+0

Wie führen Sie diese Anweisungen aus? Von einer bestimmten Programmiersprache API, eine gespeicherte Prozedur, etwas anderes? – nos

Antwort

3

Wie kann ich beide MySQL Abbruch einer Transaktion, wenn es einen Fehler auftritt, und übergeben Sie den Fehlercode an den Aufrufer?

MySQL tut Fehlercode an den Aufrufer übergeben und auf der Grundlage dieses Fehlercode die Anrufer frei zu entscheiden, ob es Arbeit bis zu dem Moment (ohne Berücksichtigung der Fehler mit dieser INSERT-Anweisung) oder ein Rollback begehen will die Transaktion.

Dies ist ungleich PostgreSQL, die die Transaktion bei Fehlern immer abbricht und dieses Verhalten eine Quelle vieler Probleme ist.

Update:

Es ist eine schlechte Praxis, eine bedingungslose ROLLBACK innerhalb der gespeicherten Prozeduren zu verwenden.

Gespeicherte Prozeduren sind stapelbar und Transaktionen nicht, daher wird ein ROLLBACK innerhalb einer geschachtelten gespeicherten Prozedur auf den Anfang der Transaktion zurückgesetzt, nicht auf den Status der Ausführung der gespeicherten Prozedur.

Wenn Sie Transaktionen verwenden, um den Datenbank-Status auf Fehler wiederherzustellen, verwenden SAVEPOINT Konstrukte und DECLARE HANDLER auf die Speicherpunkte rückgängig zu machen:

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

Ausfall in entweder Einsatz werden alle Änderungen rückgängig zu machen durch das Verfahren hergestellt und verlasse es.

+0

Soweit ich das anhand der Dokumentation feststellen konnte und nachdem ich eine Weile im Internet gesucht habe, gibt es keine Möglichkeit, den genauen Fehlercode zu erfahren. Aber wenn Sie es anders wissen, werde ich sehr gerne lernen, wie es geht. –

+0

@David: Welche Client-Bibliothek und welche Sprache verwenden Sie? – Quassnoi

+0

Ich verwende MySQL Stored Procedures. –

2

Verwendung von Mr.Quassnoi dem Beispiel, hier ist meine beste Ansatz zu fangen spezifische Fehler:

Die Idee ist es, eine tvariable zu verwenden, um eine einfache Fehlermeldung zu fangen, dann können Sie SQL-Staaten fangen Sie denken passieren können benutzerdefinierte Nachrichten an Ihre Variable speichern:

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 ; 

Dann einfach tun Sie Ihren üblichen Anruf, und führen Sie eine SELECT-Anweisung für die Variable wie:

call prc_work(); select @prc_work_error; 

Dies wird entweder NULL, wenn keine Fehler zurück, oder die Fehlermeldung im Fehlerfall. Wenn Sie eine persistente Fehlermeldung benötigen, können Sie optional eine Tabelle erstellen, um sie zu speichern.

Es ist mühsam und nicht sehr flexibel, da für jeden Statuscode, den Sie erfassen möchten, ein DECLARE EXIT HANDLER-Segment erforderlich ist. Es werden auch keine detaillierten Fehlermeldungen angezeigt, aber hey, es funktioniert.

Verwandte Themen