417

Is there to way write a T-SQL command to just make it sleep for a period of time? I am writing a web service asynchronously and I want to be able to run some tests to see if the asynchronous pattern is really going to make it more scalable. In order to "mock" an external service that is slow, I want to be able to call a SQL server with a script that runs slowly, but isn't actually processing a ton of stuff.

mwigdahl
  • 16,268
  • 7
  • 50
  • 64
skb
  • 30,624
  • 33
  • 94
  • 146

4 Answers4

718

Look at the WAITFOR command.

E.g.

-- wait for 1 minute
WAITFOR DELAY '00:01'

-- wait for 1 second
WAITFOR DELAY '00:00:01'

This command allows you a high degree of precision but is only accurate within 10ms - 16ms on a typical machine as it relies on GetTickCount. So, for example, the call WAITFOR DELAY '00:00:00:001' is likely to result in no wait at all.

Callum Watkins
  • 2,844
  • 4
  • 29
  • 49
Sam Saffron
  • 128,308
  • 78
  • 326
  • 506
  • 4
    Anybody know how to get this to work from a Function ? I get (correctly probably), but for the sake of testing I would like to override) 'Invalid use of a side-effecting operator 'WAITFOR' within a function.... – monojohnny Jun 07 '13 at 14:28
  • 2
    @monojohnny to get a SVF to wait, I've tried Josh's answer below but it didn't work. Instead I just create a WHILE loop like this: `CREATE FUNCTION [dbo].[ForcedTimeout](@seconds int) returns int as BEGIN DECLARE @endTime datetime2(0) = DATEADD(SECOND, @seconds, GETDATE()); WHILE (GETDATE() < @endTime ) BEGIN SET @endTime = @endTime; -- do nothing, but SQL requires a statement. END` – GilesDMiddleton May 15 '17 at 08:57
  • 4
    Make sure you use 3 digits for the ms - '00:00:00:01' is not equal to '00:00:00:010' use the second. (tested on MSSQL 2016) – Nick May 18 '17 at 20:36
  • 1
    you can also try BEGIN TRANSACTION and END TRANSACTION if you need to block a table – Richárd Baldauf May 05 '20 at 19:25
  • Is `'00:00:00:001'` correct? The linked documentation states that the format is `hh:mm[[:ss].mss]` with a period while this answer uses a colon. – Trisped Feb 11 '21 at 17:25
14
WAITFOR DELAY 'HH:MM:SS'

I believe the maximum time this can wait for is 23 hours, 59 minutes and 59 seconds.

Here's a Scalar-valued function to show it's use; the below function will take an integer parameter of seconds, which it then translates into HH:MM:SS and executes it using the EXEC sp_executesql @sqlcode command to query. Below function is for demonstration only, i know it's not fit for purpose really as a scalar-valued function! :-)

    CREATE FUNCTION [dbo].[ufn_DelayFor_MaxTimeIs24Hours]
    (
    @sec int
    )
    RETURNS
    nvarchar(4)
    AS
    BEGIN


    declare @hours int = @sec / 60 / 60
    declare @mins int = (@sec / 60) - (@hours * 60)
    declare @secs int = (@sec - ((@hours * 60) * 60)) - (@mins * 60)


    IF @hours > 23 
    BEGIN
    select @hours = 23
    select @mins = 59
    select @secs = 59
    -- 'maximum wait time is 23 hours, 59 minutes and 59 seconds.'
    END


    declare @sql nvarchar(24) = 'WAITFOR DELAY '+char(39)+cast(@hours as nvarchar(2))+':'+CAST(@mins as nvarchar(2))+':'+CAST(@secs as nvarchar(2))+char(39)


    exec sp_executesql @sql

    return ''
    END

IF you wish to delay longer than 24 hours, I suggest you use a @Days parameter to go for a number of days and wrap the function executable inside a loop... e.g..

    Declare @Days int = 5
    Declare @CurrentDay int = 1

    WHILE @CurrentDay <= @Days
    BEGIN

    --24 hours, function will run for 23 hours, 59 minutes, 59 seconds per run.
    [ufn_DelayFor_MaxTimeIs24Hours] 86400

    SELECT @CurrentDay = @CurrentDay + 1
    END
Josh Harris
  • 446
  • 4
  • 8
  • **SQL Azure** doesn't like this `Only functions and some extended stored procedures can be executed from within a function.` [MS Docs provide an example using Stored Procs](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql#examples) - looks like this approach is invalid – SliverNinja - MSFT Aug 10 '17 at 20:43
7

You can also "WAITFOR" a "TIME":

    RAISERROR('Im about to wait for a certain time...', 0, 1) WITH NOWAIT
    WAITFOR TIME '16:43:30.000'
    RAISERROR('I waited!', 0, 1) WITH NOWAIT
Jeremy Giaco
  • 342
  • 3
  • 5
  • 2
    Why not use `PRINT` instead of `RAISERROR`? – slartidan Nov 19 '16 at 09:49
  • 8
    Because otherwise you would see nothing until after the entire wait finished. RAISERROR gives you the NOWAIT option, so it will show you print statements (essentially) in real time, as opposed to when the buffer is full or after the batch is completed. – Jeremy Giaco Jul 20 '18 at 02:00
1

Here is a very simple piece of C# code to test the CommandTimeout with. It creates a new command which will wait for 2 seconds. Set the CommandTimeout to 1 second and you will see an exception when running it. Setting the CommandTimeout to either 0 or something higher than 2 will run fine. By the way, the default CommandTimeout is 30 seconds.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Data.SqlClient;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      var builder = new SqlConnectionStringBuilder();
      builder.DataSource = "localhost";
      builder.IntegratedSecurity = true;
      builder.InitialCatalog = "master";

      var connectionString = builder.ConnectionString;

      using (var connection = new SqlConnection(connectionString))
      {
        connection.Open();

        using (var command = connection.CreateCommand())
        {
          command.CommandText = "WAITFOR DELAY '00:00:02'";
          command.CommandTimeout = 1;

          command.ExecuteNonQuery();
        }
      }
    }
  }
}
user2192239
  • 87
  • 1
  • 2
  • 2
    If you are in c# you should probably use Thread.currentThread.sleep(60000) OR Thread.sleep(60000) which does the same thing. That way your delay is isolated to your application. Then call your subsequent database logic afterwards. – Action Dan Jul 06 '15 at 22:23
  • 4
    @ActionDan Using Thread.Sleep is not going to help exercise the CommandTimeout though, is it. As a contrived example, it does what is written on the box. – Richard Hauer Jun 19 '16 at 11:40