Wie lösche ich den PRINT-Puffer in TSQL?

220

Ich habe eine sehr lange gespeicherte Prozedur in SQL Server 2005, die ich zu debuggen versuche, und verwende dazu den Befehl 'print'. Das Problem ist, dass ich die Nachrichten erst am Ende meines Sprocs von SQL Server zurückerhalte. Ich möchte in der Lage sein, den Nachrichtenpuffer zu leeren und diese Nachrichten sofort während der Laufzeit des Sproc zu sehen, anstatt genau zu diesem Zeitpunkt Ende.

Erik Forbes
quelle
1
Nur eine kurze Nachricht für Leute, die (wie ich) denken, dass die Antworten für sie nicht funktionieren: Wechseln Sie unbedingt zur Registerkarte "Nachrichten", wenn die Abfrage ausgeführt wird. Standardmäßig wird die Registerkarte "Ergebnisse" angezeigt.
Tomasz Gandor

Antworten:

305

Verwenden Sie die RAISERRORFunktion:

RAISERROR( 'This message will show up right away...',0,1) WITH NOWAIT

Sie sollten nicht alle Ihre Drucke vollständig durch Raiserror ersetzen. Wenn Sie irgendwo eine Schleife oder einen großen Cursor haben, tun Sie dies einfach ein- oder zweimal pro Iteration oder sogar alle paar Iterationen.

Außerdem: Ich habe RAISERROR zum ersten Mal unter diesem Link kennengelernt, den ich nun als endgültige Quelle für die Behandlung von SQL Server-Fehlern betrachte und der definitiv eine Lektüre wert ist:
http://www.sommarskog.se/error-handling-I.html

Joel Coehoorn
quelle
41
Beachten Sie, dass TRY / CATCH in SQL nur Fehler mit einem Schweregrad> 10 abfängt, sodass die Verwendung von RAISERROR auf diese Weise nicht in Ihre CATCH-Anweisung springt. Das ist großartig, da Sie RAISERROR mit TRY / CATCH immer noch so verwenden können. ref: msdn.microsoft.com/en-us/library/ms175976.aspx
Rory
13
Beachten Sie, dass dies nach den ersten 500 Nachrichten nicht funktioniert. Sobald Sie mehr als das drucken, beginnt es plötzlich zu puffern!
GendoIkari
@MahmoudMoravej Nein, ich führe immer noch lang laufende Prozesse mit RAISEERROR aus und beschäftige mich nur mit der Tatsache, dass Nachrichten nach einer Weile gepuffert werden. Es scheint, dass die einzige Lösung darin besteht, ein anderes Tool als SSMS zu verwenden.
GendoIkari
1
Ich denke, das hat sich in einer neueren Version von SS geändert. Als ich dies zum ersten Mal schrieb, verwendeten wir RAISERROR für die umfassende Protokollierung von Batch-Prozessen über Nacht mit mehr als 500 Nachrichten, und das war kein Problem. Aber in 7 Jahren kann sich viel ändern.
Joel Coehoorn
1
Bei @ GendoIkari. Ich habe es mit ssms von 2016SP1 mit diesem Skript versucht. Bei 500 wird auf 50 Zeilen gepuffert und bei 1k auf jeweils 100 Zeilen umgeschaltet. Dies dauerte mindestens bis 2k, aber dann stoppte ich das Skript. deklariere @i int set @i = 0 deklariere @t varchar (100), während 1 = 1 beginne set @i = @i + 1 setze @t = 'print' + konvertiere (varchar, @i) RAISERROR (@t, 10 , 1) MIT NOWAIT Wartezeit auf Verzögerung '00: 00: 00.010 'Ende
Zartag
28

Aufbauend auf der Antwort von @JoelCoehoorn besteht mein Ansatz darin, alle meine PRINT-Anweisungen beizubehalten und ihnen einfach die RAISERROR-Anweisung zu folgen, um den Flush zu verursachen.

Beispielsweise:

PRINT 'MyVariableName: ' + @MyVariableName
RAISERROR(N'', 0, 1) WITH NOWAIT

Der Vorteil dieses Ansatzes besteht darin, dass die PRINT-Anweisungen Zeichenfolgen verketten können, während dies mit RAISERROR nicht möglich ist. (So ​​oder so haben Sie die gleiche Anzahl von Codezeilen, wie Sie eine Variable deklarieren und festlegen müssten, die in RAISERROR verwendet werden soll).

Wenn Sie wie ich AutoHotKey oder SSMSBoost oder ein gleichwertiges Tool verwenden, können Sie einfach eine Verknüpfung wie "] flush" einrichten, um die RAISERROR-Zeile für Sie einzugeben. Dies spart Ihnen Zeit, wenn es sich jedes Mal um dieselbe Codezeile handelt, dh nicht angepasst werden muss, um bestimmten Text oder eine Variable aufzunehmen.

Mike
quelle
6
Beachten Sie, dass RAISERROR()dies die printf()String-Interpolation im Stil unterstützt . Zum Beispiel, wenn @MyVariableNameein stringish Typ (zB VARCHAR(MAX), NVARCHAR(MAX)usw.), können Sie RAISERROR()mit einer Zeile: RAISERROR(N'MyVariableName: %s', 0, 1, @MyVariableName).
Binki
Das ist so bequem! Ich weiß, dass RAISERROR eine einfache Ersetzung durchführen kann, aber versuchen Sie, eine [Datums-] Uhrzeit zu ersetzen oder eine Funktion aus der RAISERROR-Anweisung heraus aufzurufen! Diese Antwort gibt Ihnen einen einfachen FLUSH in Form eines leeren Fehlers (auf Kosten einer neuen Zeile).
Tomasz Gandor
19

Ja ... Der erste Parameter der RAISERROR-Funktion benötigt eine NVARCHAR-Variable. Versuchen Sie also Folgendes:

-- Replace PRINT function
DECLARE @strMsg NVARCHAR(100)
SELECT @strMsg = 'Here''s your message...'
RAISERROR (@strMsg, 0, 1) WITH NOWAIT

ODER

RAISERROR (n'Here''s your message...', 0, 1) WITH NOWAIT
tcbrazil
quelle
10
Sehen Sie sich die Registerkarte Nachrichten unten neben der Registerkarte Ergebnisse an oder wechseln Sie in den Modus Ergebnisse zu Text.
Mehmet Ergut
Um in den Modus Ergebnisse in Text zu wechseln, wählen Sie im SSMS-Menü Extras -> Optionen -> Ergebnisse abfragen -> SQL Server -> Allgemein -> Standardziel für Ergebnisse und wählen Sie "Ergebnisse in Text" anstelle von "Ergebnisse in Raster" -Öffnen Sie das Abfragefenster und Sie werden nicht dort sitzen und eine leere Registerkarte Ergebnisse wie ein Dummy betrachten, während die RAISERROR-Ausgabe auf die Registerkarte Nachrichten wechselt.
Adam
12

Eine andere bessere Option besteht darin, sich nicht auf PRINT oder RAISERROR zu verlassen und einfach Ihre "print" -Anweisungen in eine ## Temp-Tabelle in TempDB oder eine permanente Tabelle in Ihrer Datenbank zu laden, wodurch Sie die Daten sofort über eine SELECT-Anweisung aus einem anderen Fenster sichtbar machen . Das funktioniert am besten für mich. Die Verwendung einer permanenten Tabelle dient dann auch als Protokoll für die Ereignisse in der Vergangenheit. Die print-Anweisungen sind praktisch für Fehler. Mithilfe der Protokolltabelle können Sie jedoch auch den genauen Fehlerpunkt anhand des zuletzt protokollierten Werts für diese bestimmte Ausführung ermitteln (vorausgesetzt, Sie verfolgen die Gesamtstartzeit der Ausführung in Ihrer Protokolltabelle).

Eric Isaacs
quelle
2
Dies kann ein Problem sein, wenn Sie ein echtes Transaktionsskript mit Commit und Rollback schreiben. Ich glaube nicht, dass Sie Ihre temporäre Tabelle live abfragen können - und sie verschwindet, wenn Ihre Transaktion fehlschlägt.
SteveJ
@SteveJ können Sie es live abfragen, indem Sie SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;in Ihrer Überwachungssitzung
TheConstructor
1
@TheConstructor; Das ist ein hilfreicher Tipp - ich werde das nutzen, danke. Sind wir nicht immer noch mit dem temporären Tisch beim Rollback weg? Wenn Sie eine Fehleranalyse durchführen, scheint dies ein großes Manko zu sein.
SteveJ
1
@SteveJ ja, das gibt es bestimmt. Sie können die Daten in einer READ UNCOMMITTEDTransaktion natürlich in eine andere Tabelle kopieren , aber Sie verpassen wahrscheinlich den Moment kurz zuvor ROLLBACK. Also löst es wahrscheinlich das "wie weit?" nicht das "Warum Rollback?"
TheConstructor
4

Nur als Referenz: Wenn Sie in Skripten (Stapelverarbeitung) und nicht in gespeicherten Prozeduren arbeiten , wird die Löschausgabe durch den Befehl GO ausgelöst, z

print 'test'
print 'test'
go

Im Allgemeinen lautet meine Schlussfolgerung wie folgt: Die Ausgabe der Ausführung des mssql-Skripts, die in der SMS-GUI oder mit sqlcmd.exe ausgeführt wird, wird bei der ersten GO-Anweisung oder bis zum Ende des Skripts in die Datei file, stdoutput, gui window geleert.

Das Spülen innerhalb der gespeicherten Prozedur funktioniert anders, da Sie GO nicht darin platzieren können.

Referenz: tsql Go-Anweisung

Robert Lujo
quelle
2
goLöscht nicht nur die Ausgabe, sondern beendet den Stapel gemäß dem von Ihnen angegebenen Link. Alles, was Sie declaretun, wird verworfen und ist daher für das Debuggen nicht sehr nützlich. declare @test int print "I want to read this!" go set @test=5wird, obwohl Sie einen Fehler behaupten, @testist undefiniert, weil es in einem neuen Stapel ist.
Asontu
1
Ich bin damit einverstanden, dass dies keine richtige Antwort auf diese Frage ist, aber ich habe die Antwort gegeben (siehe Haftungsausschluss am Anfang), da sie für jemand anderen nützlich sein könnte - z. B. für jemanden, der Batch-SQL ausführt.
Robert Lujo