2009-03-20 8 views
309

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

+15

buena pregunta! Es posible que desee usar esto alguna vez. Como un completo aparte, esta es la primera vez que he oído hablar de querer que el DB sea más lento;) –

+2

Estoy confundido al llamar a un servicio asincrónico desde T-SQL. – jmucchiello

Respuesta

511

vistazo a la orden WAITFOR

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 es only accurate within 10ms - 16ms en una máquina típica, ya que se basa en GetTickCount. Entonces, por ejemplo, la llamada WAITFOR DELAY '00:00:00:001' es probable que no genere esperas.

+2

¿Alguien sabe cómo hacer que esto funcione desde una función? Obtengo (correctamente, probablemente), pero por el bien de la prueba me gustaría anular) 'Uso no válido de un operador de efecto secundario' WAITFOR 'dentro de una función .... – monojohnny

+1

@monojohnny para obtener una SVF para esperar, yo' He intentado la respuesta de Josh a continuación, pero no funcionó. En su lugar, simplemente creo un ciclo WHILE como este: 'CREATE FUNCTION [dbo]. [ForcedTimeout] (@ seconds int) devuelve int como BEGIN DECLARE @endTime datetime2 (0) = DATEADD (SEGUNDO, @segundos, GETDATE()) ; WHILE (GETDATE() <@endTime) BEGIN \t SET @endTime = @endTime; - No hacer nada, pero SQL requiere una declaración. END' – GilesDMiddleton

+1

Asegúrese de utilizar 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

0

Aquí hay una pieza muy simple de código C# para probar el CommandTimeout. Crea un nuevo comando que esperará durante 2 segundos. Establezca CommandTimeout en 1 segundo y verá una excepción cuando lo ejecute. Establecer el CommandTimeout a 0 o algo superior a 2 se ejecutará correctamente. 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(); 
     } 
     } 
    } 
    } 
} 
+2

Si está en C# probablemente debería usar Thread.currentThread.sleep (60000) O Thread.sleep (60000) que hace lo mismo. De esa forma, su demora quedará aislada de su aplicación.A continuación, llame a su posterior lógica de base de datos después. –

+1

@ActionDan Usar Thread.Sleep no va a ayudar a ejercitar CommandTimeout, ¿o sí? Como un ejemplo artificial, hace lo que está escrito en la caja. –

5
WAITFOR DELAY 'HH:MM:SS' 

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

Aquí hay una función de valor escalar para mostrar su uso; la función siguiente tomará un parámetro entero de segundos, que luego traduce a HH: MM: SS y lo ejecuta usando el comando EXEC sp_executesql @sqlcode para consultar. Debajo la función es sólo para demostración, sé que no es 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 utilice un parámetro @Days ir para un número de días y envolver el ejecutable función 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 
+0

** SQL Azure ** no le gusta esto 'Solo las funciones y algunos procedimientos almacenados extendidos se pueden ejecutar desde dentro de una función. [MS Docs proporciona un ejemplo usando Stored Procs] (https://docs.microsoft.com/ en-us/sql/t-sql/language-elements/waitfor-transact-sql # examples) - parece que este enfoque no es válido – SliverNinja

1

Usted también puede "WAITFOR" 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 
+1

¿Por qué no usar 'PRINT' en lugar de' RAISERROR'? – slartidan