Comando di sospensione in T-SQL?


364

C'è un modo per scrivere un comando T-SQL per farlo dormire solo 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 modello asincrono renderà davvero più scalabile. Per "deridere" un servizio esterno che è lento, voglio essere in grado di chiamare un server SQL con uno script che gira lentamente, ma in realtà non sta elaborando un sacco di cose.


19
Domanda giusta! Potrei volerlo usare prima o poi. A parte questo, questa è la prima volta che ho mai sentito di voler rallentare il DB;)
p

2
Sono sbalordito chiamando un servizio asincrono da T-SQL.
jmucchiello,

Risposte:


616

Guarda il comando WAITFOR .

Per esempio

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

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

Questo comando consente un elevato grado di precisione, ma è preciso solo entro 10 ms - 16 ms su una macchina tipica in quanto si basa su GetTickCount . Quindi, ad esempio, WAITFOR DELAY '00:00:00:001'è probabile che la chiamata non comporti alcuna attesa.


4
Qualcuno sa come farlo funzionare da una funzione? Ottengo (probabilmente probabilmente), ma per motivi di test vorrei sovrascrivere) "Uso non valido di un operatore con effetti collaterali" WAITFOR "all'interno di una funzione ....
monojohnny

2
@monojohnny per far aspettare un SVF, ho provato la risposta di Josh qui sotto ma non ha funzionato. Invece creo solo un ciclo WHILE come questo: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
Assicurati di usare 3 cifre per il ms - '00: 00: 00: 01 'non è uguale a '00: 00: 00: 010' usa il secondo. (testato su MSSQL 2016)
Nick,

puoi anche provare BEGIN TRANSACTION e END TRANSACTION se devi bloccare un tavolo
Richárd Baldauf

9
WAITFOR DELAY 'HH:MM:SS'

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

Ecco una funzione con valori scalari per mostrare il suo uso; la funzione seguente prenderà un parametro intero di secondi, che poi tradurrà in HH: MM: SS e lo eseguirà usando il EXEC sp_executesql @sqlcodecomando per interrogare. La funzione di seguito è solo a scopo dimostrativo, so che non è adatta allo scopo in realtà come una funzione a valore 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 desideri ritardare più di 24 ore, ti suggerisco di utilizzare un parametro @Days per andare avanti per un numero di giorni e avvolgere la funzione eseguibile 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

SQL Azure non piace questo Only functions and some extended stored procedures can be executed from within a function. MS Docs fornisce un esempio usando Stored Procs - sembra che questo approccio non sia valido
SliverNinja - MSFT

5

Puoi anche "ATTENDERE" un "TEMPO":

    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

2
Perché non usare PRINTinvece di RAISERROR?
Slartidan,

4
Perché altrimenti non vedresti nulla fino al termine dell'intera attesa. RAISERROR ti offre l'opzione NOWAIT, quindi ti mostrerà le istruzioni di stampa (essenzialmente) in tempo reale, al contrario di quando il buffer è pieno o dopo che il batch è stato completato.
Jeremy Giaco,

0

Ecco un pezzo molto semplice di codice C # con cui testare CommandTimeout. Crea un nuovo comando che attenderà 2 secondi. Imposta CommandTimeout su 1 secondo e vedrai un'eccezione durante l'esecuzione. L'impostazione di CommandTimeout su 0 o qualcosa di superiore a 2 funzionerà correttamente. A proposito, il CommandTimeout predefinito è di 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 sei in c # probabilmente dovresti usare Thread.currentThread.sleep (60000) O Thread.sleep (60000) che fa la stessa cosa. In questo modo il tuo ritardo è isolato dalla tua applicazione. Quindi chiamare successivamente la logica del database successiva.
Azione Dan

2
@ActionDan L'uso di Thread.Sleep non aiuta comunque a esercitare CommandTimeout, vero? Come esempio inventato, fa ciò che è scritto sulla scatola.
Richard Hauer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.