SET işlemine katılabilecek maksimum yerel değişken sayısı nedir?


11

İş mantığı içeren bir saklı yordam var. İçinde 1609 civarında değişkenim var (nedenini sorma, motor böyle çalışıyor). SETDiğer tüm değişkenlerin bitiştirilmiş değerine bir değişken denemeye çalışıyorum . Sonuç olarak oluşturma sırasında hatayı alıyorum:

Msg 8631, Seviye 17, Durum 1, Prosedür XXX, Satır YYY Dahili hata: Sunucu yığını sınırına ulaşıldı. Lütfen sorgunuzda potansiyel olarak derin yuvalama olup olmadığına bakın ve basitleştirmeye çalışın.

Hatanın SETişlemde kullanmam gereken değişkenlerin sayısından kaynaklandığını anladım . Ödevi ikiye bölerek gerçekleştirebilirim.

Sorum şu: Bu alanda bazı kısıtlamalar var mı? Kontrol ettim, ama hiç bulamadım.

Biz açıklanan hata kontrol bu KB , ama bu bizim böyle değildir. Kodumuzda herhangi bir CASEifade kullanmıyoruz . Bu geçici değişkeni, CLR işlevi kullanılarak değiştirilmesi gereken değerlerin bir listesini hazırlamak için kullanırız. SQL Server'ımızı SP3 CU6'ya (en son güncel) güncelledik, ancak yine de hatayla karşılaşıyoruz.

Yanıtlar:


16

Msg 8631, Seviye 17, Durum 1, Satır xxx
Dahili hata: Sunucu yığını sınırına ulaşıldı.
Lütfen sorgunuzda potansiyel olarak derin yuvalama olup olmadığına bakın ve basitleştirmeye çalışın.

Bu hata, SQL Server'ın bu tür bir ifadeyi ayrıştırma ve bağlama biçimi nedeniyle iki girişli birleştirilmiş iç içe geçmiş liste olarak uzun SETveya SELECTdeğişken atama birleştirme listelerinde oluşur .

Örneğin SET @V = @W + @X + @Y + @Z, formun bir ağacına bağlanır:

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

İlk ikiden sonraki her birleştirme öğesi, bu gösterimde fazladan bir iç içe yerleştirme düzeyi ile sonuçlanır.

SQL Server tarafından kullanılabilen yığın alanı miktarı, bu iç içe yerleştirme için en büyük sınırı belirler. Sınır aşıldığında, dahili olarak bir istisna ortaya çıkar ve sonuçta yukarıda gösterilen hata mesajıyla sonuçlanır. Hata atıldığında örnek bir işlem çağrısı yığını aşağıda gösterilmiştir:

Yığın izleme

reprodüksiyon

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);

Bu, birden fazla birleştirmenin dahili olarak ele alınma biçimi nedeniyle temel bir sınırdır. Bu etkiler SETve SELECTeşit değişken atama ifadeleri.

Çözüm, tek bir ifadede gerçekleştirilen birleştirme sayısını sınırlamaktır. Bu aynı zamanda daha etkili olacaktır, çünkü derin sorgu ağaçlarını derlemek kaynak yoğundur.


5

Esinlenerek @ Paul 'in yanıt , bazı araştırmalar yapmışlar ve yığın alanı sınırı concatenations sayısı da, bu doğru ise ve bu yığın alanı bellek bir fonksiyonudur ve dolayısıyla aşağıdaki iki nokta da doğru değişir :

  1. ek birleştirmeleri tek bir ifadeye sıkıştırmanın bir yolu vardır VE
  2. ilk yığın alanı sınırlamasını aşmak için bu yöntemi kullanarak, gerçek bir mantıksal sınır (değişken gibi görünmüyor) bulunabilir

İlk olarak, Paul'ün test kodunu dizeleri birleştirmek için uyarladım:

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);

Bu testle, harika olmayan dizüstü bilgisayarımda (sadece 6 GB RAM) çalışırken alabileceğim en yüksek değer:

  • SQL Server 2017 Express Edition LocalDB (14.0.3006) kullanılarak 3311 (toplam 3312 karakter döndürür)
  • SQL Server 2012 Developer Edition SP4 (KB4018073) kullanılarak 3512 (toplam 3513 karakter döndürür) (11.0.7001)

8631 hatasını almadan önce .

Daha sonra, işlem birden çok grup birleştirmeyi birleştirecek şekilde parantez kullanarak birleştirmeleri gruplandırmayı denedim. Örneğin:

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

Bunu yaparak 3312 ve 3513 değişkenlerinin önceki sınırlarının çok ötesine geçebildim. Güncellenen kod:

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);

Maksimum değerler (benim için) 42ilki için kullanmak REPLICATE, böylece grup başına 43 değişken kullanmak ve sonra 762ikincisi için kullanmak REPLICATE, böylece her biri 43 değişkenli 762 grup kullanmaktır. İlk grup iki değişkenle kodlanmıştır.

Çıktı artık @Sdeğişkende 32.768 karakter olduğunu gösteriyor . Başlangıç ​​grubunu (@A+@A+@A)sadece yerine güncellersem (@A+@A)şu hatayı alıyorum:

Msg 8632, Seviye 17, Durum 2, Satır XXXXX
Dahili hata: Bir ifade hizmetleri sınırına ulaşıldı. Lütfen sorgunuzda potansiyel olarak karmaşık ifadeler bulun ve bunları basitleştirmeye çalışın.

Hata numarasının öncekinden farklı olduğuna dikkat edin. Şimdi: 8632 . VE, SQL Server 2012 örneğimi veya SQL Server 2017 örneğimi kullansam da aynı sınıra sahibim.

- Burada üst limiti muhtemelen tesadüf değil 32.768 - max kapasite arasında SMALLINT( Int16başlayan IF .NET içinde) 0(max değeri 32.767 olan ancak birçok / en programlama dillerinde diziler 0 tabanlıdır).


0

Şimdi, bellekte saklanan yordamın çalışması ve SQL için kullanılabilir donanım transistörleri veya Sanal Sayfa Belleği dolu olduğundan, bu sadece bellek yetersizdir!

Yani temelde SQL Server'da Stack Overflow.

Şimdi, önce 1609 Değişkenlere ihtiyacınız olduğunu bildiğimiz için, süreci basitleştirmeye çalışın,

Fakat tüm değişkenlere aynı anda mı ihtiyacınız var?

Değişkenleri gerektiğinde beyan edebilir ve kullanabiliriz.

Örneğin:

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

Ancak bunu ekleyerek bir döngüde denersek

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

Not: Bu daha fazla CPU kullanır ve hesaplamada biraz daha zaman alır.

Şimdi bu yavaş olacak, ancak daha az bellek kullanımı avantajına sahip olacak.

Umarım bu yardımcı olur, tam senaryoyu anlayabilmemiz için lütfen sorgunuzu gönderin.


-4

SET'lerin yerine SELECT ifadelerinin kullanılması performansı ve okunabilirliği artırabilir ve belirtilen hatayı aşmanıza neden olabilir. Bunun yerine:

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

Yapabilirsin:

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

Ve üç değeri de tek bir ifadede ayarlayın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.