Was ist die beste Methode, um Fehlerbehandlung in gespeicherten SQL 2005-Prozessen hinzuzufügen?

11

Was ist ein guter Weg, um gespeicherte Prozesse so robust zu machen, dass sie sehr gut skaliert werden können und auch Fehlerbehandlung enthalten?

Was ist außerdem der beste Weg, um mehrere Fehlerszenarien in einem gespeicherten Prozess zu behandeln und über ein intelligentes Feedback-System zu verfügen, das aussagekräftige Fehlerinformationen an die aufrufenden Apps zurückgibt?

Kakalapy
quelle
2
Versuchen Sie es mit dem neueren TRY CATCH-Block in SQL Server 2005. sommarskog.se/error_handling_2005.html
Sankar Reddy
Hi @Kacalapy ~ Ich möchte in Zukunft empfehlen, jede Frage für sich zu stellen. Auf diese Weise können wir spezifische Antworten erhalten, die sich jeweils auf eine Frage konzentrieren. Ich ermutige Sie, dies mit dieser Frage zu tun.
Jcolebrand

Antworten:

12

Alex Kuznetsov hat ein großartiges Kapitel in seinem Buch Defensive Database Programming (Kapitel 8), das sich mit T-SQL TRY ... CATCH, T-SQL-Transaktionen und SET XACT_ABORT-Einstellungen sowie der clientseitigen Fehlerbehandlung befasst. Es wird Ihnen sehr bei der Entscheidung helfen, welche der Optionen für das, was Sie erreichen müssen, am sinnvollsten ist.

Es ist kostenlos auf dieser Website verfügbar . Ich bin in keiner Weise mit dem Unternehmen verbunden, aber ich besitze die gedruckte Version dieses Buches.

Es gibt viele kleine Details zu diesem Thema, die von Alex sehr gut erklärt werden.

Per Nicks Bitte ... (aber nicht alles ist im Kapitel)

In Bezug auf die Skalierung müssen Sie brutal ehrlich sein, welche Aktivitäten im Datenbankcode enthalten sein müssen und welche in der App enthalten sein sollten. Haben Sie jemals bemerkt, wie schnell ausführender Code dazu neigt, für ein einzelnes Problem pro Methode zu entwerfen?

Die einfachste Art der Kommunikation wären benutzerdefinierte Fehlercodes (> 50.000). Es ist auch ziemlich schnell. Dies bedeutet, dass Sie den Datenbankcode und den App-Code synchron halten müssen. Mit einem benutzerdefinierten Fehlercode können Sie auch nützliche Informationen in der Fehlermeldung zurückgeben. Da Sie ausschließlich für diese Situation einen Fehlercode haben, können Sie einen Parser in den App-Code schreiben, der auf das Datenformat des Fehlers zugeschnitten ist.

Welche Fehlerbedingungen erfordern eine Wiederholungslogik in der Datenbank? Wenn Sie es nach X Sekunden erneut versuchen möchten, sollten Sie dies besser im App-Code behandeln, damit die Transaktion nicht so stark blockiert. Wenn Sie einen DML-Vorgang nur sofort erneut senden, kann es effizienter sein, ihn im SP zu wiederholen. Beachten Sie jedoch, dass Sie möglicherweise Code duplizieren oder eine Ebene von SPs hinzufügen müssen, um einen erneuten Versuch durchzuführen.

Wirklich, das ist derzeit der größte Schmerz bei der TRY ... CATCH-Logik in SQL Server. Es kann getan werden, aber es ist ein bisschen dumm. Suchen Sie in SQL Server 2012 nach einigen Verbesserungen, insbesondere nach erneuten Auslösen von Systemausnahmen (unter Beibehaltung der ursprünglichen Fehlernummer). Außerdem gibt es FORMATMESSAGE , die eine gewisse Flexibilität beim Erstellen von Fehlermeldungen bietet, insbesondere für Protokollierungszwecke.

Phil Helmer
quelle
Toller Rat und ein sehr gutes Buch!
Marian
Red Gate bietet mehrere äußerst hilfreiche kostenlose E-Books an, und dieses ist sicherlich eines der besseren. Toller Vorschlag.
Matt M
Nicht alle ihrer Bücher tun dies, aber die kostenlose Version von Kusnezows "Defensiv ..." - Buch enthält nicht die letzten beiden Kapitel über Transaktionsisolationsstufen und die Entwicklung von Modifikationen, die die Parallelität überleben. Für mich. Der Inhalt dort war den Kauf wert.
Phil Helmer
7

Dies ist unsere Vorlage (Fehlerprotokollierung entfernt)

Anmerkungen:

  • Ohne XACT_ABORT müssen alle TXN beginnen und Commit / Rollbacks gepaart werden
  • Ein Commit dekrementiert @@ TRANCOUNT
  • Ein Rollback bringt @@ TRANCOUNT auf Null zurück, sodass Sie den Fehler 266 erhalten
  • Sie können nicht nur die aktuelle Ebene ROLLBACKEN (z. B. @@ TRANCOUNT beim Rollback dekrementieren)
  • XACT_ABORT unterdrückt den Fehler 266
  • Jeder gespeicherte Prozess muss mit derselben Vorlage übereinstimmen, damit jeder Aufruf atomar ist
  • Die Rollback-Prüfung ist aufgrund von XACT_ABORT tatsächlich redundant. Ich fühle mich jedoch besser, sehe ohne seltsam aus und kann Situationen berücksichtigen, in denen Sie es nicht wollen
  • Dies ermöglicht clientseitige TXNs (wie LINQ).
  • Remus Rusanu hat eine ähnliche Shell , die Speicherpunkte verwendet. Ich bevorzuge einen atomaren DB-Aufruf und verwende keine Teilaktualisierungen wie deren Artikel

... erstellen Sie also nicht mehr TXNs als Sie benötigen

Jedoch,

CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON

DECLARE @starttrancount int

BEGIN TRY
    SELECT @starttrancount = @@TRANCOUNT

    IF @starttrancount = 0
        BEGIN TRANSACTION

       [...Perform work, call nested procedures...]

    IF @starttrancount = 0 
        COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
        ROLLBACK TRANSACTION
    RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
gbn
quelle
Was ist, wenn @@ TRANCOUNT größer als 0 ist? Sie arbeiten nicht oder haben kein Feedback?
Kakalapy
@kacalapy: Es gibt keine verschachtelte Transaktion, daher starten wir keine weitere scribd.com/doc/49579859/33/Nested-Transactions-Are-Real
gbn
3

Ich benutze Try / Catch, sammle aber auch so viele Informationen wie möglich und schreibe diese nach dem Rollback in ein Fehlerprotokoll. In diesem Beispiel ist "LogEvent" eine gespeicherte Prozedur, die in eine EventLog-Tabelle schreibt und die Details zu den Ereignissen enthält. GetErrorInfo () ist ein Funktionsaufruf, der die genaue Fehlermeldung zurückgibt.

Wenn ein Fehler auftritt, werden die Informationen gesammelt, die Prozedur springt zum Abschnitt zur Fehlerbehandlung und gibt einen Rollback aus. Die Informationen werden in das Protokoll geschrieben, und die Prozedur wird beendet.

In Anbetracht der zusätzlichen Prozedur- / Funktionsaufrufe scheint es etwas übertrieben. Diese Methode ist jedoch äußerst hilfreich, wenn Sie versuchen, das Problem zu beheben.

exec LogEvent @Process, @Database, 'Versuch, bla bla bla einzufügen'
BEGINNEN SIE VERSUCHEN
  in MyTable einfügen
  Wählen Sie Werte
    von MyOtherTable

  Wählen Sie @rowcount = @@ ROWCOUNT
END TRY
-- Fehlerbehandlung
FANG BEGINNEN
  Wählen Sie @error = ERROR_NUMBER (),
         @rowcount = -1,
         @TableAction = 'Einfügen',
         @TableName = @Database + '.MyTable',
         @AdditionalInfo = '(Versuch, bla bla bla einzufügen)' + dbo.GetErrorInfo ()
   GOTO TableAccessError
END CATCH

.
.
.
.

TableAccessError:
IF (@@ TRANCOUNT> 0) ROLLBACK
Wählen Sie @output = Upper (@TableAction) + 
       'ERROR - Während' + ist ein Fehler aufgetreten 
       case (@TableAction)
         Wenn 'Update', dann 'Update'
         wenn 'löschen' dann 'löschen'
         sonst @TableAction + 'ing'
       Ende + 
       'Aufzeichnungen' + 
       case (@TableAction) 
         wenn 'wähle' dann 'von' 
         wenn 'update' dann 'in' 
         wenn 'einfügen' dann 'in'
         sonst 'von'   
         Ende + 
         'die' + @TableName + 'Tabelle.'
Wählen Sie @output = @output + '@@ ERROR:' + convert (varchar (8), @ error) 
Wählen Sie @output = @output + '@@ ROWCOUNT:' + convert (varchar (8), @ rowcount) 

Wählen Sie @output = @output + isnull (@AdditionalInfo, '').
exec LogEvent @Process, @Database, @Output
RAISERROR (@ output, 16,1) mit Protokoll
Wählen Sie @ReturnCode = -1
GOTO THE_EXIT


Datagod
quelle