How to print SQL Server messages to the output immediately

When executing long-running SQL commands, it is often important to get feedback on the operations being performed.

In the context of SQL Server, the PRINT statement offers the ability to output basic informational messages that can help monitor progress and debugging.

While the PRINT statement provides a simple means of outputting a message, it can be somewhat limiting as it does not always output messages immediately. This means that messages are often not visible to the client until the overall SQL command has been completed.

If you are looking for a way to overcome this limitation, read on!

PRINT limitations

I think that one of the simplest ways to demonstrate the limitations of the PRINT statement is with the following SQL.

PRINT 'Processing...';
 
-- Simulate a long-running process.
WAITFOR DELAY '00:00:05';
 
PRINT 'Processing completed.';

Upon reviewing the above SQL, you would likely expect that the message ‘Processing…’ would be immediately output. Then, there would be a delay of 5 seconds while a simulated long-running process is in progress, and finally, the text ‘Processing completed.’ would be displayed.

However, in reality, you’ll likely find that there will be a delay of 5 seconds, followed by both the ‘Processing…’ and ‘Processing completed.’ messages being output at the end, following the delay.

You can imagine that, in the case of a more complex SQL script or stored procedure that has multiple batch operations or sub-procedures, outputting the progress of each step would be very useful for monitoring and debugging. However, if all of these progress updates were not output until the end of the process, the messages would not be of any benefit.

Overcoming limitations

To overcome the inherent limitations of the PRINT statement, we can swap things over to use the RAISERROR statement instead (yes, it in fact named RAISERROR, not RAISEERROR!)

Here is the equivalent SQL that we used in the previous section, but this time using RAISERROR instead of PRINT.

RAISERROR('Processing...', 0, 1) WITH NOWAIT;
 
-- Simulate a long-running process.
WAITFOR DELAY '00:00:05';
 
RAISERROR('Processing completed.', 0, 1) WITH NOWAIT;

The first parameter of RAISERROR is the message that will be output.

The second parameter is the severity level. By setting the severity level to zero we are indicating that although we are calling RAISERROR, this is actually just an informational-level message. A severity level greater than 10 will cause an error to be raised, resulting in control being transferred to the CATCH block when the code is contained within a TRY block.

The third parameter is the state. This can be an integer between 0 and 255 and is intended for cases where you need to identify which part of your code the ‘error’ was raised from. In our case, it doesn’t matter so we are defaulting it to 1.

WITH NOWAIT is the key part of the code that causes the message to be output immediately to the client.

Neatening things up

While it’s nice that RAISERROR solves the limitations of PRINT in relation to buffering the output, it doesn’t look very intuitive to see the text RAISERROR scattered throughout our code when we’re using it to print messages rather than raise errors.

To neaten things up, we can wrap the RAISERROR code within a stored procedure, as shown below.

CREATE OR ALTER PROCEDURE dbo.WriteOutput
    @Message NVARCHAR(MAX)
AS
BEGIN
    -- Call RAISEERROR with level 0 to indicate an informational message.
    -- Use NOWAIT to send the message immediately to the output.
    RAISERROR(@Message, 0, 1) WITH NOWAIT;
END

The stored procedure shown above accepts an @Message parameter which is in turn passed to RAISERROR.

Note that the naming of the WriteOutput procedure is inspired by the Write-Output PowerShell command. However, you may wish to choose a different name depending on your preferences.

We can update our previous SQL example for a third time as follows.

EXEC WriteOutput 'Processing...';
 
-- Simulate a long-running process.
WAITFOR DELAY '00:00:05';
 
EXEC WriteOutput 'Processing completed.';

I’m sure you’ll agree that this is much more natural to read compared to calling RAISERROR directly.

One thing to consider with the stored procedure approach is that you’ll not be able to avail of the “C printf function style formatting” functionality that RAISERROR provides. If you want to pass a concatenated string to the stored procedure, you’ll need to define a variable and pass it as the @Message parameter, as shown below.

DECLARE @Message VARCHAR(MAX) = 'Processing started at: ' + FORMAT(GETDATE(), 'yyyy-MM-dd HH:mm:ss');
EXEC WriteOutput @Message;
 
-- Simulate a long-running process.
WAITFOR DELAY '00:00:05';
 
SET @Message = 'Processing completed at: ' + FORMAT(GETDATE(), 'yyyy-MM-dd HH:mm:ss');
EXEC WriteOutput @Message;

In most cases, you’ll probably not need the advanced formatting functionality, so usually, it shouldn’t be an issue.

Summary

In this article, I explained the limitations of the SQL Server PRINT statement and demonstrated how the RAISERROR statement can be used instead to send messages immediately to the client.

I showed how you can wrap this functionality in a stored procedure to help make your SQL code read more naturally.

Lastly, I demonstrated how you can pass messages that are built up from concatenated strings using a SQL Server variable that is passed along to the stored procedure.


I hope you enjoyed this post! Comments are always welcome and I respond to all questions.

If you like my content and it helped you out, please check out the button below 🙂

Comments