2009-03-20 13 views
309

È necessario scrivere un comando T-SQL per farlo dormire per un periodo di tempo? Sto scrivendo un servizio web in modo asincrono e voglio essere in grado di eseguire alcuni test per vedere se il pattern asincrono lo renderà davvero più scalabile. Per "prendere in giro" un servizio esterno che è lento, voglio essere in grado di chiamare un server SQL con uno script che funziona lentamente, ma in realtà non sta elaborando un sacco di cose.Sleep Command in T-SQL?

+15

Fiera domanda! Potrei voler usare questo a volte. Per completezza, questa è la prima volta che ho sentito parlare di voler rallentare il DB;) –

+2

Sono impazzito chiamando un servizio asincrono da T-SQL. – jmucchiello

risposta

511

un'occhiata al comando WAITFOR

Eg.

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

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

Questo comando permette un alto grado di precisione, ma è only accurate within 10ms - 16ms su una macchina tipica in quanto si basa su GetTickCount. Ad esempio, la chiamata WAITFOR DELAY '00:00:00:001' rischia di non attendere affatto.

+2

Qualcuno sa come farlo funzionare da una funzione? Ottengo (correttamente probabilmente), ma per motivi di test vorrei eseguire l'override) 'Uso non valido di un operatore di effetti collaterali' WAITFOR 'all'interno di una funzione .... – monojohnny

+1

@monojohnny per ottenere un SVF in attesa, I' Ho provato la risposta di Josh in basso ma non ha funzionato. Invece creo solo un ciclo WHILE come questo: 'CREATE FUNCTION [dbo]. [ForcedTimeout] (@ secondi int) restituisce int come INIZIO DECLARE @endTime datetime2 (0) = DATEADD (SECONDO, @seconds, GETDATE()) ; WHILE (GETDATE() <@endTime) BEGIN \t SET @endTime = @endTime; - non fare nulla, ma SQL richiede una dichiarazione. END' – GilesDMiddleton

+1

Assicurati di utilizzare 3 cifre per il ms - '00: 00: 00: 01 'non è uguale a '00: 00: 00: 010' usa il secondo. (testato su MSSQL 2016) – Nick

0

Ecco un pezzo molto semplice di codice C# per testare CommandTimeout con. Crea un nuovo comando che attenderà per 2 secondi. Imposta CommandTimeout a 1 secondo e vedrai un'eccezione durante l'esecuzione. L'impostazione di CommandTimeout su 0 o su un valore superiore a 2 funzionerà correttamente. A proposito, il CommandTimeout predefinito è 30 secondi.

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

Se si è in C#, si dovrebbe probabilmente usare Thread.currentThread.sleep (60000) OR Thread.sleep (60000) che fa la stessa cosa. In questo modo il tuo ritardo è isolato dalla tua applicazione.Quindi chiamare la logica successiva del database in seguito. –

+1

@ActionDan L'uso di ThreadSleep non aiuta a utilizzare CommandTimeout, vero? Come esempio forzato, fa ciò che è scritto sulla scatola. –

5
WAITFOR DELAY 'HH:MM:SS' 

Credo che il tempo massimo questo può attendere è di 23 ore, 59 minuti e 59 secondi.

Ecco una funzione con valori scalari per mostrare che è in uso; la funzione sottostante prenderà un parametro intero di secondi, che poi tradurrà in HH: MM: SS e lo eseguirà usando il comando EXEC sp_executesql @sqlcode per interrogare. Sotto la funzione è solo per la dimostrazione, so che non è adatto allo scopo davvero come una funzione scalare! :-)

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 

Se si desidera ritardare più di 24 ore, vi suggerisco di utilizzare un parametro @Days di andare per un numero di giorni e avvolgere l'eseguibile funzione all'interno di un ciclo ... ad es.

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

** A SQL Azure ** non piace "Solo le funzioni e alcune stored procedure estese possono essere eseguite all'interno di una funzione. [MS Docs fornisce un esempio utilizzando Processi memorizzati] (https://docs.microsoft.com/ en-us/sql/t-sql/lingua-elements/waitfor-transact-sql # esempi) - questo approccio non è valido – SliverNinja

1

È inoltre possibile "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 
+1

Perché non usare 'PRINT' invece di' RAISERROR'? – slartidan