Schlafbefehl in T-SQL?


364

Gibt es eine Möglichkeit, einen T-SQL-Befehl zu schreiben, um ihn nur für einen bestimmten Zeitraum in den Ruhezustand zu versetzen? Ich schreibe einen Webdienst asynchron und möchte einige Tests ausführen können, um festzustellen, ob das asynchrone Muster ihn wirklich skalierbarer macht. Um einen langsamen externen Dienst zu "verspotten", möchte ich in der Lage sein, einen SQL Server mit einem Skript aufzurufen, das langsam ausgeführt wird, aber nicht wirklich eine Menge Dinge verarbeitet.


19
Faire Frage! Ich möchte dies vielleicht irgendwann verwenden. Abgesehen davon ist dies das erste Mal, dass ich davon gehört habe, dass die DB langsamer sein soll;)
p.campbell

2
Ich bin verblüfft, wenn ich einen asynchronen Dienst von T-SQL aus aufrufe.
jmucchiello

Antworten:


616

Schauen Sie sich den Befehl WAITFOR an .

Z.B

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

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

Dieser Befehl ermöglicht Ihnen ein hohes Maß an Präzision, ist jedoch auf einem typischen Computer nur innerhalb von 10 ms - 16 ms genau, da er auf GetTickCount basiert . So führt der Anruf WAITFOR DELAY '00:00:00:001'beispielsweise wahrscheinlich zu keiner Wartezeit.


4
Weiß jemand, wie man dies von einer Funktion zum Laufen bringt? Ich bekomme (wahrscheinlich richtig), aber zum Testen möchte ich überschreiben) 'Ungültige Verwendung eines Nebeneffekt-Operators' WAITFOR 'innerhalb einer Funktion ....
Monojohnny

2
@monojohnny damit ein SVF wartet, habe ich Joshs Antwort unten ausprobiert, aber es hat nicht funktioniert. Stattdessen erstelle ich einfach eine WHILE-Schleife wie 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
folgt

3
Stellen Sie sicher, dass Sie 3 Ziffern für die ms verwenden - '00: 00: 00: 01 'ist nicht gleich '00: 00: 00: 010'. Verwenden Sie die zweite. (getestet auf MSSQL 2016)
Nick

Sie können auch BEGIN TRANSACTION und END TRANSACTION ausprobieren, wenn Sie eine Tabelle blockieren müssen
Richárd Baldauf

9
WAITFOR DELAY 'HH:MM:SS'

Ich glaube, die maximale Wartezeit beträgt 23 Stunden, 59 Minuten und 59 Sekunden.

Hier ist eine skalarwertige Funktion, um ihre Verwendung zu zeigen. Die folgende Funktion benötigt einen ganzzahligen Parameter von Sekunden, der dann in HH: MM: SS übersetzt und mit dem EXEC sp_executesql @sqlcodeBefehl zum Abfragen ausgeführt wird. Die folgende Funktion dient nur zu Demonstrationszwecken. Ich weiß, dass sie nicht wirklich als skalarwertige Funktion geeignet ist. :-)

    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

Wenn Sie länger als 24 Stunden verzögern möchten, empfehlen wir Ihnen, einen @ Day-Parameter zu verwenden, um einige Tage lang die ausführbare Funktion in eine Schleife zu packen ... z.

    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 gefällt das nicht Only functions and some extended stored procedures can be executed from within a function. MS Docs bieten ein Beispiel für die Verwendung von gespeicherten Prozessen - anscheinend ist dieser Ansatz ungültig
SliverNinja - MSFT

5

Sie können auch eine "ZEIT" "WARTEN":

    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
Warum nicht PRINTstatt verwenden RAISERROR?
Slartidan

4
Denn sonst würden Sie nichts sehen, bis das gesamte Warten beendet ist. RAISERROR bietet Ihnen die Option NOWAIT, sodass Sie Druckanweisungen (im Wesentlichen) in Echtzeit anzeigen können, im Gegensatz dazu, wenn der Puffer voll ist oder wenn der Stapel abgeschlossen ist.
Jeremy Giaco

0

Hier ist ein sehr einfacher Teil des C # -Codes zum Testen des CommandTimeout. Es wird ein neuer Befehl erstellt, der 2 Sekunden wartet. Wenn Sie CommandTimeout auf 1 Sekunde einstellen, wird beim Ausführen eine Ausnahme angezeigt. Wenn Sie CommandTimeout auf 0 oder höher als 2 setzen, funktioniert dies einwandfrei. Das Standard-CommandTimeout beträgt übrigens 30 Sekunden.

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
Wenn Sie sich in c # befinden, sollten Sie wahrscheinlich Thread.currentThread.sleep (60000) ODER Thread.sleep (60000) verwenden, das dasselbe tut. Auf diese Weise wird Ihre Verzögerung auf Ihre Anwendung beschränkt. Rufen Sie anschließend Ihre nachfolgende Datenbanklogik auf.
Aktion Dan

2
@ActionDan Die Verwendung von Thread.Sleep wird das CommandTimeout jedoch nicht unterstützen, oder? Als erfundenes Beispiel macht es das, was auf der Box steht.
Richard Hauer
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.