Comando de suspensión en T-SQL?

364

¿Hay alguna manera de escribir un comando T-SQL para hacerlo dormir por un período de tiempo? Estoy escribiendo un servicio web de forma asincrónica y quiero poder ejecutar algunas pruebas para ver si el patrón asincrónico realmente lo hará más escalable. Para "simular" un servicio externo que es lento, quiero poder llamar a un servidor SQL con un script que se ejecuta lentamente, pero en realidad no está procesando un montón de cosas.

skb
fuente
19
¡Buena pregunta! Quizás quiera usar esto alguna vez. Como un aparte completo, esta es la primera vez que escucho querer que el DB sea más lento;)
p.campbell
2
Estoy aturdido llamando a un servicio asincrónico desde T-SQL.
jmucchiello

Respuestas:

616

Mira el comando WAITFOR .

P.ej

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

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

Este comando le permite un alto grado de precisión, pero solo es preciso entre 10 ms y 16 ms en una máquina típica, ya que se basa en GetTickCount . Entonces, por ejemplo, WAITFOR DELAY '00:00:00:001'es probable que la llamada no dé lugar a ninguna espera.

Sam Azafrán
fuente
44
¿Alguien sabe cómo hacer que esto funcione desde una función? Obtengo (probablemente correctamente), pero en aras de las pruebas me gustaría anular) 'Uso no válido de un operador de efectos secundarios' WAITFOR 'dentro de una función ...
monojohnny
2
@monojohnny para que un SVF espere, probé la respuesta de Josh a continuación, pero no funcionó. En cambio, solo creo un ciclo WHILE como este: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
3
Asegúrese de usar 3 dígitos para el ms - '00: 00: 00: 01 'no es igual a '00: 00: 00: 010' use el segundo. (probado en MSSQL 2016)
Nick
también puedes probar BEGIN TRANSACTION y END TRANSACTION si necesitas bloquear una mesa
Richárd Baldauf
9
WAITFOR DELAY 'HH:MM:SS'

Creo que el tiempo máximo que esto puede esperar es de 23 horas, 59 minutos y 59 segundos.

Aquí hay una función de valor escalar para mostrar su uso; la siguiente función tomará un parámetro entero de segundos, que luego se traducirá a HH: MM: SS y lo ejecutará usando el EXEC sp_executesql @sqlcodecomando para consultar. La siguiente función es solo para demostración, ¡sé que no es adecuada para el propósito realmente como una función de valor escalar! :-)

    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

SI desea retrasar más de 24 horas, le sugiero que use un parámetro @Days para pasar varios días y envolver la función ejecutable dentro de un bucle ... por ejemplo.

    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
fuente
A SQL Azure no le gusta esto. Only functions and some extended stored procedures can be executed from within a function. MS Docs proporciona un ejemplo usando Procs almacenados ; parece que este enfoque no es válido
SliverNinja - MSFT
5

También puede "ESPERAR" un "TIEMPO":

    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
fuente
2
¿Por qué no usar en PRINTlugar de RAISERROR?
slartidan
44
Porque de lo contrario no verías nada hasta que terminara toda la espera. RAISERROR le ofrece la opción NOWAIT, por lo que le mostrará declaraciones de impresión (esencialmente) en tiempo real, a diferencia de cuando el búfer está lleno o después de que se completa el lote.
Jeremy Giaco
0

Aquí hay una pieza muy simple de código C # para probar CommandTimeout. Crea un nuevo comando que esperará 2 segundos. Establezca CommandTimeout en 1 segundo y verá una excepción cuando lo ejecute. Establecer CommandTimeout en 0 o en algo superior a 2 funcionará bien. Por cierto, el CommandTimeout predeterminado es de 30 segundos.

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();
        }
      }
    }
  }
}
usuario2192239
fuente
2
Si está en C #, probablemente debería usar Thread.currentThread.sleep (60000) O Thread.sleep (60000), que hace lo mismo. De esa manera, su retraso está aislado de su aplicación. A continuación, llame a la lógica de su base de datos posterior.
Acción Dan
2
@ActionDan Usar Thread.Sleep no va a ayudar a ejercer CommandTimeout, ¿verdad? Como ejemplo artificial, hace lo que está escrito en la caja.
Richard Hauer