Was ist die maximale Anzahl lokaler Variablen, die an der SET-Operation teilnehmen können?


11

Ich habe eine gespeicherte Prozedur, die Geschäftslogik enthält. Darin habe ich ungefähr 1609 Variablen (frag mich nicht warum, so funktioniert der Motor). Ich versuche SETeine Variable auf den verketteten Wert aller anderen Variablen zu setzen. Als Ergebnis erhalte ich während der Erstellung den Fehler:

Meldung 8631, Ebene 17, Status 1, Prozedur XXX, Zeile JJJ Interner Fehler: Server-Stack-Limit wurde erreicht. Suchen Sie in Ihrer Abfrage nach potenziell tiefen Verschachtelungen und versuchen Sie, diese zu vereinfachen.

Ich habe herausgefunden, dass der Fehler auf die Anzahl der Variablen zurückzuführen ist, die ich für die SETOperation verwenden muss. Ich kann die Aufgabe ausführen, indem ich sie in zwei Teile aufteile.

Meine Frage ist, gibt es einige Einschränkungen in diesem Bereich? Ich habe nachgesehen, aber keine gefunden.

Wir haben den in dieser KB beschriebenen Fehler überprüft , dies ist jedoch nicht unser Fall. Wir verwenden keine CASEAusdrücke in unserem Code. Wir verwenden diese temporäre Variable, um eine Liste von Werten zu erstellen, die mithilfe einer CLR-Funktion ersetzt werden müssen. Wir haben unseren SQL Server auf SP3 CU6 aktualisiert (aktuell), aber der Fehler tritt weiterhin auf.

Antworten:


16

Meldung 8631, Ebene 17, Status 1, Zeile xxx
Interner Fehler: Server-Stack-Limit wurde erreicht.
Suchen Sie in Ihrer Abfrage nach potenziell tiefen Verschachtelungen und versuchen Sie, diese zu vereinfachen.

Dieser Fehler tritt bei langen SEToder SELECTvariablen Zuordnungsverkettungslisten auf, da SQL Server diese Art von Anweisung analysiert und bindet - als verschachtelte Liste von Verkettungen mit zwei Eingaben.

Zum Beispiel SET @V = @W + @X + @Y + @Zist in einen Baum der Form gebunden:

ScaOp_Arithmetic x_aopAdd
    ScaOp_Arithmetic x_aopAdd
        ScaOp_Arithmetic x_aopAdd
            ScaOp_Identifier @W 
            ScaOp_Identifier @X 
        ScaOp_Identifier @Y 
    ScaOp_Identifier @Z 

Jedes Verkettungselement nach den ersten beiden führt zu einer zusätzlichen Verschachtelungsebene in dieser Darstellung.

Der für SQL Server verfügbare Stapelspeicher bestimmt die endgültige Grenze für diese Verschachtelung. Wenn das Limit überschritten wird, wird intern eine Ausnahme ausgelöst, die schließlich zu der oben gezeigten Fehlermeldung führt. Ein Beispiel für einen Prozessaufrufstapel, wenn der Fehler ausgelöst wird, ist unten dargestellt:

Stapelspur

Repro

DECLARE @SQL varchar(max);

SET @SQL = '
    DECLARE @S integer, @A integer = 1; 
    SET @S = @A'; -- Change to SELECT if you like

SET @SQL += REPLICATE(CONVERT(varchar(max), ' + @A'), 3410) +';'; -- Change the number 3410

-- SET @S = @A + @A + @A...
EXECUTE (@SQL);

Dies ist eine grundlegende Grenze, da mehrere Verkettungen intern behandelt werden. Es wirkt sich gleichermaßen auf SETund SELECTVariablenzuweisungsanweisungen aus.

Die Problemumgehung besteht darin, die Anzahl der in einer einzelnen Anweisung ausgeführten Verkettungen zu begrenzen. Dies ist in der Regel auch effizienter, da das Kompilieren tiefer Abfragebäume ressourcenintensiv ist.


5

Inspiriert von der Antwort von @Paul , habe ich einige Nachforschungen angestellt und festgestellt, dass der Stapelspeicher zwar die Anzahl der Verkettungen begrenzt, und der Stapelspeicher eine Funktion des verfügbaren Speichers ist und daher variiert. Die folgenden beiden Punkte sind jedoch ebenfalls zutreffend ::

  1. Es gibt eine Möglichkeit, zusätzliche Verkettungen in eine einzige Anweisung UND zu packen
  2. Wenn Sie diese Methode verwenden, um über die anfängliche Stapelplatzbeschränkung hinauszugehen, kann eine tatsächliche logische Grenze (die nicht zu variieren scheint) gefunden werden

Zuerst habe ich Pauls Testcode angepasst, um Zeichenfolgen zu verketten:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'
    DECLARE @S VARCHAR(MAX), @A VARCHAR(MAX) = ''a''; 
    SET @S = @A';

SET @SQL += REPLICATE(CONVERT(NVARCHAR(MAX), N' + @A'), 3312) + N';';

-- SET @S = @A + @A + @A...
SET @SQL += N'SELECT DATALENGTH(@S) AS [Chars In @S];';
EXECUTE (@SQL);

Mit diesem Test konnte ich auf meinem nicht so großartigen Laptop (nur 6 GB RAM) höchstens Folgendes erreichen:

  • 3311 (gibt insgesamt 3312 Zeichen zurück) mit SQL Server 2017 Express Edition LocalDB (14.0.3006)
  • 3512 (gibt insgesamt 3513 Zeichen zurück) mit SQL Server 2012 Developer Edition SP4 (KB4018073) (11.0.7001)

bevor Fehler 8631 angezeigt wird .

Als nächstes habe ich versucht, die Verkettungen mithilfe von Klammern so zu gruppieren, dass die Operation mehrere Gruppen von Verkettungen verkettet. Beispielsweise:

SET @S = (@A + @A + @A + @A) + (@A + @A + @A + @A) + (@A + @A + @A + @A);

Auf diese Weise konnte ich die bisherigen Grenzen von 3312 und 3513 Variablen weit überschreiten. Der aktualisierte Code lautet:

DECLARE @SQL VARCHAR(MAX), @Chunk VARCHAR(MAX);

SET @SQL = '
    DECLARE @S VARCHAR(MAX), @A VARCHAR(MAX) = ''a''; 
    SET @S = (@A+@A)';

SET @Chunk = ' + (@A' + REPLICATE(CONVERT(VARCHAR(MAX), '+@A'), 42) + ')';

SET @SQL += REPLICATE(CONVERT(VARCHAR(MAX), @Chunk), 762) + ';';

SET @SQL += 'SELECT DATALENGTH(@S) AS [Chars In @S];';

-- PRINT @SQL; -- for debug

-- SET @S = (@A+@A) + (@A + @A...) + ...
EXECUTE (@SQL);

Die Maximalwerte (für mich) sind jetzt 42für die erste zu verwenden REPLICATE, also 43 Variablen pro Gruppe, und dann 762für die zweite REPLICATE, also 762 Gruppen mit jeweils 43 Variablen. Die Anfangsgruppe ist mit zwei Variablen fest codiert.

Die Ausgabe zeigt nun, dass die @SVariable 32.768 Zeichen enthält . Wenn ich die ursprüngliche Gruppe so aktualisiere, dass sie (@A+@A+@A)nicht nur ist (@A+@A), wird folgende Fehlermeldung angezeigt:

Meldung 8632, Ebene 17, Status 2, Zeile XXXXX
Interner Fehler: Ein Limit für Ausdrucksdienste wurde erreicht. Suchen Sie in Ihrer Abfrage nach potenziell komplexen Ausdrücken und versuchen Sie, diese zu vereinfachen.

Beachten Sie, dass die Fehlernummer anders ist als zuvor. Es ist jetzt: 8632 . UND, ich habe das gleiche Limit, unabhängig davon, ob ich meine SQL Server 2012-Instanz oder die SQL Server 2017-Instanz verwende.

Es ist wahrscheinlich kein Zufall, dass die Obergrenze hier - 32.768 - die maximale Kapazität von SMALLINT( Int16in .NET) IF ab 0(der maximale Wert ist 32.767, aber Arrays in vielen / den meisten Programmiersprachen sind 0-basiert) ist.


0

Mit anderen Worten, dies ist einfach nicht genügend Speicher, da der Vorgang der gespeicherten Prozedur im Speicher und der verfügbaren Hardware-Transistoren oder des für SQL verfügbaren virtuellen Seitenspeichers voll ist!

Es ist also im Grunde ein Stapelüberlauf in SQL Server.

Versuchen Sie nun zunächst, den Prozess zu vereinfachen, da wir wissen, dass Sie 1609 Variablen benötigen.

Aber brauchen Sie alle Variablen gleichzeitig?

Bei Bedarf können wir Variablen deklarieren und verwenden.

Beispielsweise:

Declare @var1 int, @Var2 int @Var3 int, .... , @var1000 int; -- Here assume Thousand Variables are declared

Declare @Tot Int;
SET @Tot = 0;
if(True)
Begin
    SET @TOT = @TOT+ VAR1 + VAR2 + .... + VAR1000; -- This might fail; 
End

Aber wenn wir dies in einer Schleife versuchen, indem wir hinzufügen

Declare @Tot Int;
SET @Tot = 0;
DECLARE @i int, @Count int;
SET @i = 1;
SET @Count = 1609;
WHILE (@i <= @Count)
BEGIN
   DECLARE @SQL NVARCHAR(128);
   SET @SQL = 'SET @TOT = @TOT+ VAR'+ cast(@i as nvarchar);
   EXEC (@SQL);
   SET @i = @i + 1;
END

Hinweis: Dies verbraucht mehr CPU und benötigt etwas mehr Zeit für die Berechnung.

Dies ist jetzt langsam, hat aber den Vorteil einer geringeren Speichernutzung.

Ich hoffe, das hilft. Bitte posten Sie Ihre Anfrage, damit wir das genaue Szenario verstehen können.


-4

Die Verwendung von SELECT-Anweisungen anstelle von SETs kann die Leistung und Lesbarkeit verbessern und den angegebenen Fehler umgehen. Also statt:

SET @a = 1
SET @b = 2
SET @c = @e + 2*@d

Du kannst tun:

SELECT @a = 1, @b = 2, @c = @e + 2 * @d

Und setzen Sie alle drei Werte in einer Anweisung.

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.