Ilu z Was korzystało z polecenia PRINT?
A ilu na jego podstawie oparło logikę powiadamiania użytkownika uruchamiającego procedurę/batcha o rozpoczęciu/zakończeniu jakiegoś kroku w kodzie?
Podejrzewam, że znajdzie się kilka takich osób… 🙂
No dobrze, a czy zaobserwowaliście jakieś nieprawidłowości w wyświetlaniu komunikatów? Nie?
A spróbujcie uruchomić poniższy kod:
DECLARE @i INT = 0; WHILE @i <= 10 BEGIN PRINT @i SET @i += 1 WAITFOR DELAY '00:00:01' END
Cóż on robi? Jest zmienna typu INT, pętelka z PRINTem pokazująca naszą @i, SET zmieniający jej wartość (+1) i sekundowa pauza.
Po uruchomieniu powinniśmy w oknie Messages co sekundę dostawać liczby od 0 do 10. I co? Dostaliście? 😛 Owszem, są … ale wyświetliły się dopiero jak pętla skończyła swoją ostatnią iterację…
Dlaczego?
Niestety PRINT, podobnie jak to ma miejsce w innych językach programowania np. C, jest buforowany. To oznacza, że wszystkie napisy, które kierujecie do PRINTa, w celu optymalizacji najpierw są odkładane w specjalnym miejscu i dopiero jak będzie tego zbyt dużo to przekazywane są do użytkownika na ekran.
To może spowodować złą interpretację wyświetlanych logów i kompletnie namieszac Wam w zrozumieniu czasów wykonywania kodu, zazwyczaj koniec czegoś co sobie oznaczyliście w kodzie PRINTem następuje dużo wcześniej niż to co Wam SQL Server oznajmia w Management Studio.
Jak zatem to obejść?
Istnieje pewne polecenie, które choć przeznaczone do zwracania błędów, to potrafi zwrócić ciąg znaków bez traktowania go jako błąd (nie przerywa wam skryptu) i to w czasie takim, jak dla błędu – czyli natychmiast!
Jest to RAISERROR z opcją WITH NOWAIT oraz parametrami 0 dla severity i 1 dla state. Komunikat musi być napisem, zatem musimy uprzednio wykonać cast @i do napisu (RAISERROR nie pozwala na użycie funkcji wewnątrz swoich parametrów)
Patrząc na nasz przykład z @i, zastępujemy naszego PRINTa w następujący sposób:
DECLARE @i INT = 0; DECLARE @i_napis VARCHAR(2) WHILE @i <= 10 BEGIN SET @i_napis = CAST(@i AS VARCHAR(2)) RAISERROR( @i_napis,0,1) WITH NOWAIT SET @i += 1 WAITFOR DELAY '00:00:01' END
CIEKAWOSTKA 😀
RAISERROR za treść komunikatu może przyjąć napis zawierający formatowanie podobne do funkcji stringf w C. W treści komunikatu, wystawiając parametry %s i %d, możemy przekazać wartości parametrów poprzez dodanie ich jako argumentów do polecenia. %s sluży do podstawiania napisów, %d do liczb.
„Innymi słowy” możemy dla naszego przykładu wykonać coś takiego:
DECLARE @i INT = 0; WHILE @i <= 10 BEGIN RAISERROR( 'Wartość %s %s: %d' ,0 -- severity ,1 -- state , 'naszej liczby' -- pierwszy argument: pierwsze %s , 'wynosi' -- drugi argument: drugie %s , @i -- trzeci argument: pierwsze %d ) WITH NOWAIT SET @i += 1 WAITFOR DELAY '00:00:01' END
Wynikiem jest napis zwracany co sekundę, po zakończeniu dostaniemy: