Dies geschieht, weil ein Laufzeitsyntaxfehler, die nicht in einem nicht TRY
/CATCH
gewickelt ist keine aktive Transaktion ON
abbrechen, auch mit XACT_ABORT
Satz. Die genauen Regeln für das, was abbricht und nicht abbricht, und unter welchen Umständen, sind nicht einfach oder offensichtlich. Erland Sommarskog hat an excellent write-up on error handling in general und the rules of what does and doesn't abort in particular.
Ich werde alle nicht reproduzieren, die hier, aber hier ist das Problem auf das Wesentliche kocht:
SET XACT_ABORT ON -- or OFF, it makes no difference
BEGIN TRANSACTION
EXEC ('SELECT') -- Incorrect syntax near 'SELECT'
PRINT @@TRANCOUNT -- Prints 1, transaction is still going
COMMIT
PRINT @@TRANCOUNT -- Prints 0, transaction succeeded
Trotz XACT_ABORT ON
, Ausführung nicht nur nicht zu stoppen, wird die Transaktion nicht einmal abgebrochen. Hinzufügen TRY
/CATCH
ändert die Regeln:
SET XACT_ABORT ON
BEGIN TRANSACTION
BEGIN TRY
EXEC ('SELECT') -- Incorrect syntax near 'SELECT'
PRINT 'After bad statement.' -- Does not print
COMMIT
END TRY
BEGIN CATCH
PRINT @@TRANCOUNT -- Prints 1, transaction is still going, but it's doomed
END CATCH
-- Error here:
-- 'Uncommittable transaction is detected at the end of the batch.
-- The transaction is rolled back.'
Jetzt wird die Transaktion zum Scheitern verurteilt, und wenn wir es uns nicht zurückrollen, SQL Server macht es für uns (mit einem Fehler). Dieses dooming ist mit freundlicher Genehmigung vollständig aus den XACT_ABORT
, weil es aus Ausbeuten etwas andere noch einmal drehen:
SET XACT_ABORT OFF
BEGIN TRANSACTION
BEGIN TRY
EXEC ('SELECT') -- Incorrect syntax near 'SELECT'
PRINT 'After bad statement.' -- Does not print
COMMIT
END TRY
BEGIN CATCH
PRINT @@TRANCOUNT -- Prints 1, transaction is still going
END CATCH
PRINT @@TRANCOUNT -- Prints 1, transaction is still going!
ROLLBACK
Die Moral der Geschichte ist: richtige Fehlerbehandlung in T-SQL ist sehr schwierig. Was normalerweise für mich funktioniert, ist SET XACT_ABORT ON
für alle nicht-trivialen Batch-Anweisungen zu tun und Transaktionen (außerhalb des SQL-Servers) zu initiieren und zu committen oder rückgängig zu machen (über Client-Code). Dies umgeht viele der Schwierigkeiten beim Verständnis dessen, was eine Transaktion stoppt und unterbindet, da jeder Fehler, den SQL Server an den Client zurückgibt, letztendlich zu einem Rollback führt. Aber selbst das ist natürlich keine Wunderwaffe.
Sieht aus wie es hängt davon ab, was "etwas Unsinn" ist, wenn es ein Kompilierzeitfehler ist, verhält es sich wie du sagst. Wenn es sich um einen Laufzeitfehler handelt (zB 'exec sp_executesql N'SELECT 1/0'') verhält es sich wie gewünscht. –
Ok, ich wusste nicht, dass das einen Unterschied machen würde. Ich hatte einen Syntaxfehler, und das würde es erklären. Gibt es jedoch eine Dokumentation, die erklären würde, warum das so ist? Ich würde immer noch erwarten, dass die gesamte Transaktion zurückgesetzt wird, selbst wenn es sich um einen Fehler bei der Kompilierung handelte. Oder behandelt die Option XACT_ABORT nur Laufzeitfehler? – Pinetree
Ich war mir dieses Verhalten vorher nicht bewusst. Ich weiß nicht, ob es "von Entwurf" oder nicht ist. –