SQL Server: Ein scheinbar seltsames Verhalten von BEGIN TRAN - COMMIT

7

Erstellen Sie zunächst zwei Tabellen:

CREATE TABLE xyz (Id INT)
CREATE TABLE abc (Id INT)

Bitte beachten Sie den folgenden SQL-Code:

DELETE FROM abc
BEGIN TRAN
EXEC('
INSERT INTO xyz VALUES (1),(2)
DECLARE @x INT = (SELECT Id FROM xyz)
')
INSERT INTO abc VALUES (1)
COMMIT

SELECT * FROM abc

Beim Ausführen wird der folgende Fehler ausgegeben:

Msg 512, Level 16, State 1, Line 3
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

Und es gibt kein Ergebnis . Alles wie erwartet.

Nun lassen Sie uns den Fehler zu einem Syntaxfehler ändern durch Ersetzen xyz)mit xyz. Hier ist der Fehler:

Msg 102, Level 15, State 1, Line 3
Incorrect syntax near 'xyz'.

Aber diesmal gibt es ein Ergebnis:

Geben Sie hier die Bildbeschreibung ein

Warum? Was ist los?

Kennzeichen
quelle
5
Können Sie erklären, warum Sie diese Frage erneut eingeführt haben ?
Aaron Bertrand
Ich bin mir nicht sicher, was das richtige Forum ist - SO oder DBA. Nach der Anzahl der Aufrufe zu urteilen, die es auf SO gepostet haben, war wahrscheinlich in erster Linie ein Fehler.
Markieren Sie den

Antworten:

6

Sie können das Verhalten Ihres ersten Beispiels steuern, indem Sie Folgendes angeben:

SET XACT_ABORT ON

Am Anfang Ihres Skripts erhalten Sie kein Ergebnis. Sie erhalten ein Ergebnis, wie Sie es beschreiben, wenn Sie angeben SET XACT_ABORT OFF.

Der Grund für das unterschiedliche Verhalten ist, dass das zweite Beispiel "Ersetzen xyz)durch xyz" einen Syntaxfehler enthält, dh der EXEC-Stapel wird analysiert, ein Syntaxfehler entdeckt und der Stapel wird nicht ausgeführt. Die EXEC ist ein separater Stapel (aus diesem Grund können Sie Variablen außerhalb von EXEC nicht definieren und innerhalb von EXEC referenzieren). Die Verarbeitung wird mit dem Rest der Transaktion fortgesetzt.

Für das erste Beispiel " (SELECT Id FROM xyz)" ist dies eine gültige Syntax, sodass der Stapel korrekt analysiert wird. Der Fehler wird nur zur Laufzeit gefunden und die Verarbeitung wird fortgesetzt, wenn, SET XACT_ABORT OFFaber wenn gestoppt SET XACT_ABORT ON.

Andy Jones
quelle
Gibt es eine Möglichkeit, alle Fehler gleich zu behandeln, ohne auf BEGIN TRY-END CATCH zurückzugreifen, was zu einem ausführlicheren Code führt - man muss manuell @@TRANCOUNTzurücksetzen (das Internet sagt, ich muss es vorher überprüfen ) und den ursprünglichen Fehler erneut auslösen mit THROW. Viel Code.
Markieren Sie den
9

Das Verhalten hängt von SET XACT_ABORT ab und wird im folgenden Zitat aus dem verlinkten Artikel erläutert.

Wenn SET XACT_ABORT aktiviert ist und eine Transact-SQL-Anweisung einen Laufzeitfehler auslöst, wird die gesamte Transaktion beendet und zurückgesetzt.

Wenn SET XACT_ABORT deaktiviert ist, wird in einigen Fällen nur die Transact-SQL-Anweisung, die den Fehler ausgelöst hat, zurückgesetzt und die Transaktion wird weiter verarbeitet. Abhängig von der Schwere des Fehlers kann die gesamte Transaktion zurückgesetzt werden, auch wenn SET XACT_ABORT AUS ist. AUS ist die Standardeinstellung.

Mikael Eriksson
quelle