Jak opróżnić bufor PRINT w TSQL?

220

Mam bardzo długo działającą procedurę przechowywaną w SQL Server 2005, którą próbuję debugować i używam do tego polecenia „print”. Problem polega na tym, że odbieram wiadomości z SQL Server tylko na samym końcu mojego sproc - chciałbym móc opróżnić bufor komunikatów i zobaczyć te komunikaty natychmiast podczas uruchamiania sproca, a nie na samym początku koniec.

Erik Forbes
źródło
1
Krótkie powiadomienie dla osób, które (jak ja) będą myśleć, że odpowiedzi nie działają dla nich: pamiętaj, aby przejść do zakładki „Wiadomości”, gdy zapytanie jest uruchomione. Domyślnie zobaczysz kartę „Wyniki”.
Tomasz Gandor,

Odpowiedzi:

305

Użyj RAISERRORfunkcji:

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

Nie powinieneś całkowicie zamieniać wszystkich swoich wydruków na horror. Jeśli masz gdzieś pętlę lub duży kursor, po prostu zrób to raz lub dwa razy na iterację, a nawet co kilka iteracji.

Ponadto: po raz pierwszy dowiedziałem się o RAISERROR pod tym linkiem, który teraz uważam za ostateczne źródło obsługi błędów SQL Server i zdecydowanie warto przeczytać:
http://www.sommarskog.se/error-handling-I.html

Joel Coehoorn
źródło
41
Zauważ, że TRY / CATCH w SQL wychwytuje błędy tylko o istotności> 10, więc użycie RAISERROR w ten sposób nie przeskoczy do twojej instrukcji CATCH. Co jest świetne, ponieważ oznacza to, że nadal możesz używać RAISERROR w ten sposób z TRY / CATCH. ref: msdn.microsoft.com/en-us/library/ms175976.aspx
Rory
13
Pamiętaj, że to nie działa po pierwszych 500 wiadomościach; gdy wydrukujesz więcej, nagle zacznie się buforować!
GendoIkari,
@MahmoudMoravej Nie, nadal uruchamiam długo działające procesy przy użyciu RAISEERROR i po prostu mam do czynienia z faktem, że po pewnym czasie wiadomości zaczynają być buforowane. Wydaje się, że jedynym rozwiązaniem byłoby użycie innego narzędzia niż SSMS.
GendoIkari,
1
Myślę, że to zmieniło się w najnowszej wersji SS. Dawno temu, kiedy pierwszy raz to napisałem, użyliśmy RAISERROR do obszernego rejestrowania nocnych procesów wsadowych z wieloma ponad 500 komunikatami, i to nie był problem. Ale wiele może się zmienić w ciągu 7 lat.
Joel Coehoorn
1
Na zawiadomienie @ GendoIkari. Próbowałem tego z ssms od 2016SP1 z tym skryptem. Przy 500 przełącza się na buforowanie 50 linii, a przy 1k przełącza na 100 linii każda. Trwało to przynajmniej do 2k, ale potem zatrzymałem skrypt. deklaruj @i int ustaw @i = 0 deklaruj @ t varchar (100), podczas gdy 1 = 1 zacznij ustaw @i = @i + 1 ustaw @t = 'drukuj + konwersja (varchar, @i) RAISERROR (@t, 10 , 1) Z NOWAIT czekać na opóźnienie '00: 00: 00.010' koniec
Zartag
28

Opierając się na odpowiedzi @JoelCoehoorn, moim podejściem jest pozostawienie wszystkich moich instrukcji PRINT na miejscu i po prostu stosowanie się do nich za pomocą instrukcji RAISERROR, aby spowodować kolor.

Na przykład:

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

Zaletą tego podejścia jest to, że instrukcje PRINT mogą łączyć łańcuchy, podczas gdy RAISERROR nie. (Tak czy inaczej, masz taką samą liczbę wierszy kodu, jak będziesz musiał zadeklarować i ustawić zmienną do użycia w RAISERROR).

Jeśli, podobnie jak ja, używasz AutoHotKey lub SSMSBoost lub równoważnego narzędzia, możesz łatwo skonfigurować skrót, taki jak „] flush”, aby wprowadzić dla siebie linię RAISERROR. Oszczędza to czas, jeśli jest to ten sam wiersz kodu za każdym razem, tzn. Nie trzeba go dostosowywać, aby przechowywał określony tekst lub zmienną.

Mikrofon
źródło
6
Zauważ, że RAISERROR()obsługuje printf()interpolację ciągów w stylu. Na przykład, jeśli @MyVariableNamejest typem stringish (np VARCHAR(MAX), NVARCHAR(MAX)etc.), można skorzystać RAISERROR()z jednej linii: RAISERROR(N'MyVariableName: %s', 0, 1, @MyVariableName).
binki
To takie wygodne! Wiem, że RAISERROR może dokonać prostej zamiany, ale spróbuj podstawić czas [data] lub wywołać funkcję z wnętrza instrukcji RAISERROR! Ta odpowiedź daje prosty PŁUKANIE w postaci zgłaszania pustego błędu (kosztem nowej linii).
Tomasz Gandor,
19

Tak ... Pierwszy parametr funkcji RAISERROR wymaga zmiennej NVARCHAR. Więc spróbuj następujących;

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

LUB

RAISERROR (n'Here''s your message...', 0, 1) WITH NOWAIT
tcbrazil
źródło
10
Spójrz na kartę Wiadomości na dole, obok zakładki Wyniki lub przejdź do trybu Wyniki na tekst.
Mehmet Ergut,
Aby przełączyć do trybu Wyniki na tekst, w SSMS, menu Narzędzia -> Opcje -> Wyniki zapytania -> SQL Server -> Ogólne -> Domyślne miejsce docelowe wyników i wybierz „Wyniki na tekst” zamiast „Wyniki na siatki”, ponownie - otwórz okno zapytania, a wtedy nie będziesz siedzieć i patrzeć na pustą kartę Wyniki jak manekin, gdy wyjście RAISERROR przejdzie do karty Wiadomości.
Adam
12

Inną lepszą opcją jest nie poleganie na DRUKOWANIU lub RAISERROR, a jedynie załadowanie instrukcji „print” do tabeli ## Temp w TempDB lub stałej tabeli w bazie danych, która zapewni natychmiastową widoczność danych za pośrednictwem instrukcji SELECT z innego okna . To działa najlepiej dla mnie. Użycie stałego stołu służy również jako dziennik wydarzeń z przeszłości. Instrukcje drukowania są przydatne w przypadku błędów, ale za pomocą tabeli dziennika można również określić dokładny punkt awarii na podstawie ostatniej zarejestrowanej wartości dla tego konkretnego wykonania (zakładając, że śledzisz ogólny czas rozpoczęcia wykonywania w tabeli dziennika).

Eric Isaacs
źródło
2
Może to stanowić problem, jeśli piszesz prawdziwie transakcyjny skrypt z zatwierdzaniem i wycofywaniem. Nie sądzę, że będziesz w stanie sprawdzić na żywo tabelę tymczasową - i zniknie, jeśli transakcja się nie powiedzie.
SteveJ,
@ SteveJ możesz wysłać zapytanie na żywo za pomocą SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;sesji monitorowania
TheConstructor
1
@TheConstructor; To przydatna wskazówka - skorzystam z tego, dzięki. Czy jednak nie pozostało nam, że tabela tymczasowa odchodzi po przywróceniu? Przeprowadzanie analizy awarii wydaje się być dużym niedociągnięciem.
SteveJ,
1
@ SteveJ tak, z pewnością jest to. Możesz oczywiście skopiować dane z READ UNCOMMITTEDtransakcji do innej tabeli, ale prawdopodobnie przegapisz chwilę wcześniej ROLLBACK. Prawdopodobnie rozwiązuje to pytanie „jak daleko?” nie „dlaczego wycofać?”
TheConstructor
4

Tylko dla odniesienia, jeśli pracujesz w skryptach (przetwarzanie wsadowe), a nie w procedurze przechowywanej , dane wyjściowe opróżniania są wyzwalane przez komendę GO, np.

print 'test'
print 'test'
go

Ogólnie rzecz biorąc, mój wniosek jest następujący: dane wyjściowe wykonania skryptu mssql, uruchamiane w graficznym interfejsie SMS lub za pomocą sqlcmd.exe, są opróżniane do pliku, stdoutput, okno GUI na pierwszej instrukcji GO lub do końca skryptu.

Płukanie wewnątrz procedury przechowywanej działa inaczej, ponieważ nie można umieścić GO w środku.

Odniesienie: instrukcja tsql Go

Robert Lujo
źródło
2
gonie tylko opróżnia wyjście, ale kończy partię zgodnie z podanym linkiem. Cokolwiek declared zostanie odrzucone, więc nie jest bardzo przydatne do debugowania. declare @test int print "I want to read this!" go set @test=5będzie zgłaszać błąd, twierdząc, że błąd @testjest niezdefiniowany, ponieważ jest w nowej partii.
asontu
1
Zgadzam się, to nie jest właściwa odpowiedź na to pytanie, ale umieszczam odpowiedź (patrz wyłączenie odpowiedzialności na początku), ponieważ może być przydatna dla kogoś innego - np. Kogoś, kto uruchamia pakiet wsadowy.
Robert Lujo,