SET ऑपरेशन में भाग लेने वाले स्थानीय चर की अधिकतम संख्या क्या है?


11

मेरे पास एक संग्रहीत प्रक्रिया है जिसमें व्यावसायिक तर्क हैं। इसके अंदर मेरे पास लगभग 1609 वैरिएबल हैं (मुझसे यह न पूछें कि, यह इंजन कैसे काम करता है)। मैं SETअन्य सभी चर के संक्षिप्त मूल्य के लिए एक चर की कोशिश करता हूं । निर्माण के दौरान मुझे त्रुटि मिलती है:

Msg 8631, स्तर 17, राज्य 1, प्रक्रिया XXX, लाइन YYY आंतरिक त्रुटि: सर्वर स्टैक सीमा तक पहुँच गया है। कृपया अपनी क्वेरी में संभावित रूप से गहरी नेस्टिंग देखें, और इसे सरल बनाने का प्रयास करें।

मुझे पता चला कि त्रुटि SETऑपरेशन में उपयोग किए जाने वाले चर की संख्या के कारण है । मैं इसे दो भागों में विभाजित करके कार्य कर सकता हूं।

मेरा सवाल है कि क्या इस क्षेत्र में कुछ प्रतिबंध हैं? मैंने जाँच की, लेकिन मुझे कोई नहीं मिला।

हमने इस KB में वर्णित त्रुटि की जाँच की , लेकिन यह हमारा मामला नहीं है। हम CASEअपने कोड के अंदर किसी भी भाव का उपयोग नहीं करते हैं । हम उस अस्थायी चर का उपयोग उन मानों की सूची तैयार करने के लिए करते हैं जिन्हें सीएलआर फ़ंक्शन का उपयोग करके प्रतिस्थापित किया जाना है। हमने अपने एसक्यूएल सर्वर को एसपी 3 सीयू 6 (नवीनतम अप टू डेट) अपडेट किया, लेकिन हम अभी भी त्रुटि का अनुभव करते हैं।

जवाबों:


16

Msg 8631, स्तर 17, राज्य 1, लाइन xxx
आंतरिक त्रुटि: सर्वर स्टैक सीमा तक पहुँच गया है।
कृपया अपनी क्वेरी में संभावित रूप से गहरी नेस्टिंग देखें, और इसे सरल बनाने का प्रयास करें।

यह त्रुटि SQL सर्वर पार्स जिस तरह से और इस प्रकार के कथन को बांधने के कारण होती है, लंबी SETया SELECTचर असाइनमेंट कॉन्टेक्स्टेंशन सूचियों के साथ होती है - दो-इनपुट कॉन्टेक्नेशन्स की नेस्टेड सूची के रूप में।

उदाहरण के लिए, SET @V = @W + @X + @Y + @Zफार्म के एक पेड़ में बंधा है:

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

इस प्रतिनिधित्व में घोंसले के एक अतिरिक्त स्तर में पहले दो परिणामों के बाद प्रत्येक संघनन तत्व।

SQL सर्वर के लिए उपलब्ध स्टैक स्थान की मात्रा इस नेस्टिंग की अंतिम सीमा निर्धारित करती है। जब सीमा पार हो जाती है, तो एक अपवाद आंतरिक रूप से उठाया जाता है, जो अंततः ऊपर दिखाए गए त्रुटि संदेश में परिणाम करता है। जब त्रुटि डाली जाती है एक उदाहरण प्रक्रिया कॉल स्टैक नीचे दिखाया गया है:

स्टैक ट्रेस

रेप्रो

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

यह उस तरह की मूलभूत सीमा है जिस तरह से कई संघटन आंतरिक रूप से नियंत्रित होते हैं। यह समान रूप से प्रभावित करता है SETऔर SELECTअसाइनमेंट स्टेटमेंट्स को वेरिएबल करता है ।

वर्कअराउंड एक स्टेटमेंट में किए गए कॉन्टेक्शंस की संख्या को सीमित करना है। यह आमतौर पर अधिक कुशल भी होगा, क्योंकि गहरे क्वेरी पेड़ों को संकलित करना संसाधन-गहन है।


5

@Paul के उत्तर से प्रेरित होकर , मैंने कुछ शोध किए और पाया कि यह सच है कि स्टैक स्पेस कॉन्टेक्टेशन की संख्या को सीमित करता है, और यह स्टैक स्पेस उपलब्ध मेमोरी का एक फ़ंक्शन है और इस प्रकार बदलता है, निम्नलिखित दो बिंदु भी सत्य हैं :

  1. एक बयान में अतिरिक्त संघों को रटने का एक तरीका है, और
  2. प्रारंभिक स्टैक स्थान सीमा से परे जाने के लिए इस पद्धति का उपयोग करके, एक वास्तविक तार्किक सीमा (जो भिन्न नहीं दिखाई देती है) पाई जा सकती है

सबसे पहले, मैंने पॉल के टेस्ट कोड को स्ट्रिंग को बदलने के लिए अनुकूलित किया:

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

इस परीक्षण के साथ, मेरे सबसे बड़े लैपटॉप (केवल 6 जीबी रैम) पर चलने पर मुझे सबसे ज्यादा मिल सकता था:

  • 3311 (SQL सर्वर 2017 एक्सप्रेस संस्करण LocalDB (14.0.3006) का उपयोग करके 3333 कुल रिटर्न)
  • SQL सर्वर 2012 डेवलपर संस्करण SP4 (KB4018073) (11.0.7001) का उपयोग करके 3512 (कुल 3513 रिटर्न)

8631 त्रुटि होने से पहले ।

इसके बाद, मैंने कोष्ठक का उपयोग करके संघों को समूहीकृत करने की कोशिश की, जैसे कि संचालन संघों के कई समूहों का संघटन होगा। उदाहरण के लिए:

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

यह करते हुए कि मैं 3312 और 3513 चर की पिछली सीमाओं से परे जाने में सक्षम था। अद्यतन कोड है:

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

अधिकतम मान (मेरे लिए) अब 42पहले के लिए उपयोग करने के लिए हैं REPLICATE, इस प्रकार प्रति समूह 43 चर का उपयोग करना, और फिर 762दूसरे के लिए उपयोग करना REPLICATE, इस प्रकार प्रत्येक 43 चर के 762 समूहों का उपयोग करना है। प्रारंभिक समूह दो चर के साथ हार्ड-कोडित है।

आउटपुट अब दिखाता है कि @Sचर में 32,768 अक्षर हैं । यदि मैं प्रारंभिक समूह को (@A+@A+@A)केवल के बजाय अद्यतन करता हूं (@A+@A), तो मुझे निम्न त्रुटि मिलती है:

Msg 8632, स्तर 17, राज्य 2, लाइन XXXXX
आंतरिक त्रुटि: एक अभिव्यक्ति सेवाओं की सीमा समाप्त हो गई है। कृपया अपनी क्वेरी में संभावित जटिल अभिव्यक्तियों की तलाश करें, और उन्हें सरल बनाने का प्रयास करें।

ध्यान दें कि त्रुटि संख्या पहले की तुलना में भिन्न है। यह अब है: 8632 । और, मेरी एक ही सीमा है कि क्या मैं अपने SQL Server 2012 इंस्टेंस या SQL Server 2017 इंस्टेंस का उपयोग करता हूं।

यह शायद कोई संयोग नहीं है कि यहां ऊपरी सीमा - 32,768 - ( .NET में) की अधिकतम क्षमता है SMALLINT( Int16यदि 0अधिकतम मूल्य 32,767 है, लेकिन कई / अधिकांश प्रोग्रामिंग भाषाओं में सरणियाँ 0-आधारित हैं)।


0

अब, यह केवल दूसरे शब्दों में मेमोरी से बाहर है, क्योंकि मेमोरी में किए गए संग्रहीत कार्यविधि का संचालन और एसक्यूएल के लिए उपलब्ध हार्डवेयर ट्रांजिस्टर या वर्चुअल पेज मेमोरी पूर्ण है!

तो इसका मूल रूप से SQL सर्वर में ढेर अतिप्रवाह।

अब, पहले प्रक्रिया को सरल बनाने का प्रयास करें, क्योंकि हम जानते हैं कि आपको 1609 चर की आवश्यकता है,

लेकिन क्या आपको एक ही समय में सभी चर की आवश्यकता है?

हम जहां जरूरत है, चर घोषित और उपयोग कर सकते हैं।

उदाहरण के लिए:

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

लेकिन अगर हम जोड़कर एक पाश में यह कोशिश करते हैं

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

नोट: यह अधिक CPU का उपयोग करेगा, और गणना में थोड़ा और समय लेगा।

अब यह धीमा होगा, लेकिन कम मेमोरी उपयोग का लाभ होगा।

मुझे उम्मीद है कि यह आपकी मदद करेगा, कृपया अपनी क्वेरी पोस्ट करें ताकि हम सटीक परिदृश्य को समझ सकें।


-4

SET के बजाय SELECT स्टेटमेंट का उपयोग करने से प्रदर्शन और पठनीयता में सुधार हो सकता है, और आपको बताई गई त्रुटि के आसपास मिल सकती है। इसलिए इसके बजाय:

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

तुम कर सकते हो:

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

और एक बयान में सभी तीन मान सेट करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.