Polecenie uśpienia w T-SQL?

364

Czy istnieje sposób, aby napisać polecenie T-SQL, aby uśpić go na pewien czas? Piszę usługę internetową asynchronicznie i chcę móc uruchomić kilka testów, aby sprawdzić, czy wzorzec asynchroniczny naprawdę sprawi, że będzie bardziej skalowalny. Aby „wyśmiewać” wolną usługę zewnętrzną, chcę móc wywoływać serwer SQL za pomocą skryptu, który działa wolno, ale tak naprawdę nie przetwarza wielu rzeczy.

skb
źródło
19
Uczciwe pytanie! Mogę kiedyś tego użyć.
Podsumowując,
2
Zadziwiają mnie wywołanie usługi asynchronicznej z T-SQL.
jmucchiello,

Odpowiedzi:

616

Spójrz na polecenie WAITFOR .

Na przykład

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

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

To polecenie pozwala na wysoki stopień precyzji, ale jest dokładne tylko w ciągu 10ms - 16ms na typowej maszynie, ponieważ opiera się na GetTickCount . Na przykład połączenie WAITFOR DELAY '00:00:00:001'prawdopodobnie nie spowoduje żadnego oczekiwania.

Sam Saffron
źródło
4
Czy ktoś wie, jak to zrobić z funkcji? Dostaję (prawdopodobnie poprawnie), ale ze względu na testowanie chciałbym zastąpić) „Nieprawidłowe użycie operatora pobocznego„ WAITFOR ”w funkcji ....
monojohnny
2
@monojohnny, aby SVF czekał, próbowałem odpowiedzi Josha poniżej, ale to nie zadziałało. Zamiast tego po prostu tworzę pętlę WHILE taką: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
Upewnij się, że używasz 3 cyfr dla ms - „00: 00: 00: 01” nie jest równe „00: 00: 00: 010” użyj drugiej. (testowany na MSSQL 2016)
Nick
możesz także spróbować ROZPOCZNIĆ TRANSAKCJĘ i KONIEC TRANSAKCJI, jeśli chcesz zablokować stolik
Richárd Baldauf
9
WAITFOR DELAY 'HH:MM:SS'

Uważam, że maksymalny czas, na który może czekać to 23 godziny, 59 minut i 59 sekund.

Oto funkcja o wartości skalarnej pokazująca, że ​​jest używana; poniższa funkcja pobiera parametr liczby całkowitej w sekundach, który następnie tłumaczy na GG: MM: SS i wykonuje go za pomocą EXEC sp_executesql @sqlcodepolecenia zapytania. Poniższa funkcja służy wyłącznie do celów demonstracyjnych, wiem, że nie nadaje się ona tak naprawdę do funkcji o wartości skalarnej! :-)

    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

JEŚLI chcesz opóźnić dłużej niż 24 godziny, sugeruję użycie parametru @Days, aby przejść przez kilka dni i zawinąć funkcję wykonywalną w pętli ... np.

    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
źródło
SQL Azure nie lubi tego Only functions and some extended stored procedures can be executed from within a function. MS Docs podaje przykład przy użyciu Stored Procs - wygląda na to, że to podejście jest nieprawidłowe
SliverNinja - MSFT
5

Możesz także „OCZEKIWAĆ” na „CZAS”:

    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
źródło
2
Dlaczego nie użyć PRINTzamiast RAISERROR?
slartidan,
4
W przeciwnym razie nic nie zobaczysz, dopóki nie skończy się całe oczekiwanie. RAISERROR udostępnia opcję NOWAIT, dzięki czemu będzie wyświetlać instrukcje (zasadniczo) w czasie rzeczywistym, a nie wtedy, gdy bufor jest pełny lub po zakończeniu partii.
Jeremy Giaco,
0

Oto bardzo prosty fragment kodu C # do przetestowania CommandTimeout. Tworzy nowe polecenie, które będzie czekać 2 sekundy. Ustaw CommandTimeout na 1 sekundę, a zobaczysz wyjątek podczas jego uruchamiania. Ustawienie CommandTimeout na wartość 0 lub wartość wyższą niż 2 będzie działać poprawnie. Nawiasem mówiąc, domyślny CommandTimeout to 30 sekund.

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();
        }
      }
    }
  }
}
użytkownik2192239
źródło
2
Jeśli jesteś w c #, prawdopodobnie powinieneś użyć Thread.currentThread.sleep (60000) LUB Thread.sleep (60000), który robi to samo. W ten sposób Twoje opóźnienie jest odizolowane od Twojej aplikacji. Następnie wywołaj kolejną logikę bazy danych.
Action Dan
2
@ActionDan Korzystanie z Thread.Sleep nie pomoże jednak w ćwiczeniu CommandTimeout, prawda? Jako wymyślony przykład robi to, co jest napisane na pudełku.
Richard Hauer,