PRINT vs RAISERROR

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:

POST TEN ZOSTAŁ PIERWOTNIE OPUBLIKOWANY JAKO TSQL NA DZIŚ #17

Dodaj komentarz