Using the NOWAIT option with the SQL Server RAISERROR statement

By:   |   Comments (11)   |   Related: > Error Handling


Problem

T-SQL scripts and stored procedures don't produce their PRINT statements and RAISERROR output in a way that keeps you informed of the code's progress. You usually have to wait until the procedure is complete before seeing messages.  How can I get insight into the code's progress?

Solution

The solution is to use the WITH NOWAIT clause of the RAISERROR statement.  It may be surprising but using RAISERROR doesn't require that there is an error condition.  If the severity level given to RAISERROR is 0 through 10 SQL Server treats the RAISERROR as a plain message and not an error at all.  Execution continues with the next statement, even if there is a TRY/CATCH block or if SET XACT_ABORT is ON.  Use the combination of a RAISERROR severity of 0 to 10 and the WITH NOWAIT clause for a statement that sends output to the Messages windows immediately, like this one:

RAISERROR ('Now that''s what I call a message!', 0, 1) WITH NOWAIT

Don't be deceived if messages are obscured by the Results windows of SSMS or the Grids windows in Query Analyzer. When query results are being sent to a grid, these panes are shown as soon as command execution begins and the Messages window is hidden unless there are no results. There are two ways to address this. One option is to send the results to text using either the menu or CTRL+T.  The other option is to allow  results to go to the grid and click on the messages window or use the SSMS menu command Window/Next Pane, which by default is tied to the F6 key or Shift+F6 in Query Analyzer.

Once you've sent Results to Text with CTRL+T try this script:

DECLARE @time char(8)
PRINT '1 PRINT before anything else      ' 
                + convert (varchar(30), getdate(), 8)
SET @time= convert (varchar(30), getdate(), 8)
RAISERROR ('2 RAISERROR before WITHOUT NOWAIT %s', 0, 1, @time) 
WAITFOR DELAY '00:00:05'
PRINT '3 PRINT after the first delay     ' 
                 + convert (varchar(30), getdate(), 8)
SET @time= convert (varchar(30), getdate(), 8)
RAISERROR ('4 RAISERROR with NOWAIT           %s', 0, 1, @time) 
        WITH NOWAIT
WAITFOR DELAY '00:00:10'
PRINT '5 PRINT after the second delay    ' 
                     + convert (varchar(30), getdate(), 8)

What you'll see is something like this result:

1 PRINT before anything else      15:47:14
2 RAISERROR before WITHOUT NOWAIT 15:47:14
3 PRINT after the first delay     15:47:19
4 RAISERROR with NOWAIT           15:47:19

These results show up in 5 seconds.  Neither the PRINT statements on lines 1 and 3 nor the RAISERROR on line 2 show up before the RAISERROR WITH NOWAIT on line 4 is executed. Then after another 10 seconds you'll see:

5 PRINT after the second delay    15:47:29

As you can see from the time stamps, lines 1 and 2 were executed at 15:47:14 then there was a 5 second delay until 15:47:19 when lines 3 and 4 are executed and appear.  It's the NOWAIT clause on the RAISERROR that produces line 4 that forces lines 1 through 4 to the message window.

One way to make the NOWAIT clause convenient is to write it into a simple stored procedure and I use this one frequently:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE proc [dbo].[ns_log_nowait]  
   @Msg nvarchar(2047)
/* Send a message to the caller so that it's available 
* immediately.
*
* example
print 'before the call'
exec dbo.ns_log_nowait 'This is the immediate message'
waitfor delay '00:00:10'
print 'This is after a 10 second delay'
compare this to
print 'before the call'
raiserror ('this is the msg', 0, 1)
waitfor delay '00:00:10'
print 'This is after a 10 second delay'
***********************************************************/
AS
    RAISERROR (@Msg, 0, 1) WITH NOWAIT 
go 
GRANT EXECUTE on [dbo].[ns_log_nowait] to PUBLIC
go

RAISERROR with severity under 11 isn't treated as an error in a TRY/CATCH construct and NOWAIT has no effect on this behavior.  To see that RAISERROR with severity 0 is treated like a print statement try this script in SSMS:

DECLARE @time char(8)
BEGIN TRY
    PRINT '1 PRINT in the TRY block ' 
            + convert (varchar(30), getdate(), 8)
    SET @time= convert (varchar(30), getdate(), 8)
    RAISERROR ('2 RAISERROR with NOWAIT %s', 0 --<< Severity 0
               , 1, @time) WITH NOWAIT
    WAITFOR DELAY '00:00:05'
END TRY 
BEGIN CATCH
    PRINT '3 PRINT in the CATCH block ' 
         + convert (varchar(30), getdate(), 8)
    PRINT '3A Error Number = ' +convert (VARCHAR, ERROR_NUMBER())
        + ' Severity = ' + convert (varchar, ERROR_SEVERITY())
        + ' Msg = ''' + ERROR_MESSAGE() + ''''
END CATCH 
PRINT '4 PRINT after the CATCH block ' 
+ convert (varchar(30), getdate(), 8)

The output from this script is shown here:

1 PRINT in the TRY block      16:03:04
2 RAISERROR with NOWAIT       16:03:04
4 PRINT after the CATCH block 16:03:09

Notice that lines 3 and 3A, in the CATCH block aren't reached even though a RAISERROR was executed.  However, if you change the severity on the RAISERROR to 11, the statement is treated as an error.  Here's the script again with only the severity of the RAISERROR changed:

DECLARE @time char(8)
BEGIN TRY
 
   PRINT '1 PRINT in the TRY block      ' 
                + convert (varchar(30), getdate(), 8)
   SET @time= convert (varchar(30), getdate(), 8)
   RAISERROR ('2 RAISERROR with NOWAIT       %s', 11 --<< Severity 11
           , 1, @time) WITH NOWAIT
   WAITFOR DELAY '00:00:05'
END TRY 
BEGIN CATCH
   PRINT '3 PRINT in the CATCH block    ' 
                + convert (varchar(30), getdate(), 8)
   PRINT '3A Error Number = ' +convert (VARCHAR, ERROR_NUMBER())
         + ' Severity = ' + convert (varchar, ERROR_SEVERITY())
         + ' Msg = ''' + ERROR_MESSAGE() + ''''
END CATCH 
PRINT '4 PRINT after the CATCH block ' 
                + convert (varchar(30), getdate(), 8)

Which produces this output:

1 PRINT in the TRY block      16:11:13
3 PRINT in the CATCH block    16:11:13
3A Error Number = 50000 Severity = 11 Msg = '2 RAISERROR with NOWAIT  16:11:13'
4 PRINT after the CATCH block 16:11:13

Notice that the RAISERROR statement is handled by the TRY/CATCH and doesn't produce message output on its own.  Instead the error is handled by the CATCH block, which has code that prints the message on line 3A along with the severity.

If you move around between versions of SQL Server you'll be pleased to know that NOWAIT works in SQL Server 2000, 2005, and 2008. The behavior seems to be the same in all versions of SQL Server.

Next Steps


sql server categories

sql server webinars

subscribe to mssqltips

sql server tutorials

sql server white papers

next tip



About the author
MSSQLTips author Andy Novick Andy Novick is a SQL Server Developer in the Boston area with 25 years of database and application development experience.

This author pledges the content of this article is based on professional experience and not AI generated.

View all my tips



Comments For This Article




Tuesday, November 16, 2021 - 10:35:46 AM - Aaron C Back To Top (89452)
This is in addition to the comment i posted. What I meant earlier was eventhough RaiseError with NO WAIT is working, it just DOES NOT output the intermediate PRINT statements within Store procedure during the sequential processing of the steps, rather it outputs the PRINT results only during the END completion of the stored procedure. Does anyone face the same with Azure Synapse dedicated pool and have a probable solution for the same.

Wednesday, November 10, 2021 - 3:04:09 AM - Aaron C Back To Top (89430)
RaiseError with NOWAIT is not working for Azure SQL DW(Azure Synpase) for dedicated pool as on 11Nov2021. Can someone else confirm the same who is using Azure Synpase?

Tuesday, February 2, 2016 - 3:02:21 PM - joely Back To Top (40575)

Carefull: when SP is called by DTS integration service the package will return error using this method. (learned it the hard way...)


Monday, November 2, 2015 - 10:08:36 AM - mahendra Back To Top (39004)

Begin

DECLARE @SPPROG varchar(30), @NOW datetime

SET FLUSHMESSAGE ON

SELECT @SPPROG = "ebrUpdate",

           @NOW = getdate()

PRINT "%1! - begin %2!", @SPPROG, @NOW

    declare @cnt  int

    select @cnt = count(*) from YBS_ST..BS_ABC_OI where FLAG = 'Y'

    if ( @cnt <= @chkThresholdNum )

    begin

        PRINT "%1! -  no needs to make bulk updating.  %2!", @SPPROG, @chkThresholdNum

        set rowcount 0

        PRINT "%1! - end %2!", @SPPROG, @NOW

        return 0

    end

    PRINT "%1! -  need to make bulk updating.", @SPPROG

 

-- Please i want to know how to repalce the SET FLUSHMESSAGE ON from the query..


Wednesday, August 27, 2014 - 11:33:54 AM - Dave Hanks Back To Top (34284)

I've been looking for this for a long time. THank you for your article, it was just what I needed.


Tuesday, April 29, 2014 - 9:54:38 PM - Samuel Fu Back To Top (30554)

Hi, Andy,

I try the code fragment on SQL 2014 CTP2, but the SSMS display all 5 lines after 15 sec, rather than displaying the line 1-4 first after 5 sec and then display the last line after 10 sec. I got the same behaviour on SQL 2008R2. Any idea?

1 PRINT before anything else      09:42:42
2 RAISERROR before WITHOUT NOWAIT 09:42:42
3 PRINT after the first delay     09:42:47
4 RAISERROR with NOWAIT           09:42:47
5 PRINT after the second delay    09:42:57

Thanks for help,

Samuel

 


Wednesday, April 2, 2014 - 8:19:17 PM - Wraith Back To Top (29950)

Doesn't work properly after the first 500 lines of RAISERROR WITH NOWAIT messages!

Starting from the 501-th line, it will only return the output once every 50 lines. And from the 1001-th line, it will only return the output once every 100 lines...


Wednesday, February 12, 2014 - 8:16:27 AM - Roger Tranchez Back To Top (29416)

Thanks !!!

This is ideal for troubleshooting when for some reason you can't debug T-SQL, or when you're stuck with something that takes too much time inside a stored procedure and you get confused.

Roger


Monday, January 13, 2014 - 4:15:50 PM - Stan Back To Top (28046)

Output of PRINT or RAISERROR surrounded by square brackets, like SET @time= '['+convert (varchar(30), getdate(), 8)+']', will be suppressed in job history or in the output file of job step. However it works ok if you just exec the stored procedure.


Thursday, January 8, 2009 - 6:40:07 AM - Senthilkumar.S Back To Top (2531)

 


Wednesday, January 7, 2009 - 1:40:42 PM - ScottPletcher Back To Top (2521)

Fantastic article!  Thank you.















get free sql tips
agree to terms