จำนวนตัวแปรท้องถิ่นที่สามารถมีส่วนร่วมในการดำเนินงานของตลาดหลักทรัพย์สูงสุดคือเท่าไร


11

ฉันมีขั้นตอนการจัดเก็บที่มีตรรกะทางธุรกิจ ข้างในนั้นฉันมีตัวแปรประมาณ 1609 (อย่าถามฉันว่าทำไมนี่คือวิธีการทำงานของเครื่องยนต์) ฉันพยายามSETตัวแปรหนึ่งตัวต่อค่าที่เชื่อมโยงกันของตัวแปรอื่นทั้งหมด เป็นผลในระหว่างการสร้างฉันได้รับข้อผิดพลาด:

ข่าวสารเกี่ยวกับ 8631, ระดับ 17, สถานะ 1, กระบวนงาน XXX, บรรทัด YYY ข้อผิดพลาดภายใน: ถึงขีด จำกัด สแต็กเซิร์ฟเวอร์แล้ว โปรดค้นหาการซ้อนที่อาจเกิดขึ้นในแบบสอบถามของคุณและพยายามทำให้มันง่ายขึ้น

ฉันพบว่าข้อผิดพลาดเกิดจากจำนวนตัวแปรที่ฉันต้องใช้ในการSETดำเนินการ ฉันสามารถทำงานที่มอบหมายโดยแยกออกเป็นสองส่วน

คำถามของฉันมีข้อ จำกัด บางประการในเรื่องนี้หรือไม่? ฉันตรวจสอบแล้ว แต่ไม่พบสิ่งใด

เราตรวจสอบข้อผิดพลาดที่อธิบายในKBนี้ แต่นี่ไม่ใช่กรณีของเรา เราไม่ใช้CASEการแสดงออกใด ๆภายในรหัสของเรา เราใช้ตัวแปรชั่วคราวนั้นเพื่อเตรียมรายการค่าที่ต้องถูกแทนที่โดยใช้ฟังก์ชัน CLR เราอัปเดต SQL Server เป็น SP3 CU6 (รุ่นล่าสุด) แต่เรายังคงพบข้อผิดพลาด

คำตอบ:


16

เกี่ยวกับข่าวสาร 8631 ระดับ 17 สถานะ 1 บรรทัด xxx
ข้อผิดพลาดภายใน: ถึงขีด จำกัด ของสแต็กเซิร์ฟเวอร์แล้ว
โปรดค้นหาการซ้อนที่อาจเกิดขึ้นในแบบสอบถามของคุณและพยายามทำให้มันง่ายขึ้น

ข้อผิดพลาดนี้เกิดขึ้นกับรายการการต่อเชื่อมแบบกำหนดค่าแบบยาวSETหรือแบบSELECTผันแปรเนื่องจากวิธีที่ SQL Server แยกวิเคราะห์และผูกคำสั่งประเภทนี้ - เป็นรายการซ้อนกันของการต่อข้อมูลแบบสองอินพุต

ตัวอย่างเช่น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 Server จะกำหนดขีด จำกัด ขั้นสุดท้ายสำหรับการซ้อนนี้ เมื่อเกินขีด จำกัด จะมีการยกข้อยกเว้นภายในซึ่งจะส่งผลให้เกิดข้อผิดพลาดที่แสดงไว้ด้านบน ตัวอย่างการเรียกใช้โปรเซสเมื่อข้อผิดพลาดถูกแสดงดังด้านล่าง:

การติดตามสแต็ก

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

นี่เป็นข้อ จำกัด พื้นฐานเนื่องจากมีการจัดการการเรียงต่อกันหลายอย่างภายใน มันมีผลต่อ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);

จากการทดสอบนี้จำนวนสูงสุดที่ฉันจะได้รับเมื่อใช้งานบนแล็ปท็อปที่ไม่ค่อยดี (RAM เพียง 6 GB) คือ:

  • 3311 (ส่งกลับ 3312 ตัวอักษรทั้งหมด) โดยใช้ SQL Server 2017 Express Edition LocalDB (14.0.3006)
  • 3512 (ส่งคืน 354 ตัวอักษรทั้งหมด) โดยใช้ SQL Server 2012 Developer Edition SP4 (KB4018073) (11.0.7001)

ก่อนที่จะรับข้อผิดพลาด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จึงใช้ 762 กลุ่ม 43 ตัวแปรแต่ละ กลุ่มเริ่มต้นถูกฮาร์ดโค้ดด้วยตัวแปรสองตัว

ตอนนี้ผลลัพธ์แสดงให้เห็นว่ามี@Sตัวแปรในอักขระ 32,768 ตัว หากฉันอัปเดตกลุ่มเริ่มต้นให้เป็น(@A+@A+@A)เพียงแค่(@A+@A)ฉันจะได้รับข้อผิดพลาดต่อไปนี้:

ข่าวสารเกี่ยวกับ 8632, ระดับ 17, สถานะ 2,
ข้อผิดพลาดภายในบรรทัด XXXXX : ข้อ จำกัด ของนิพจน์บริการถึงแล้ว โปรดค้นหานิพจน์ที่ซับซ้อนที่อาจเกิดขึ้นในแบบสอบถามของคุณและลองทำให้มันง่ายขึ้น

ขอให้สังเกตว่าหมายเลขข้อผิดพลาดแตกต่างจากก่อนหน้านี้ คือตอนนี้: 8632 และฉันมีข้อ จำกัด เดียวกันนี้ไม่ว่าฉันจะใช้อินสแตนซ์ SQL Server 2012 ของฉันหรืออินสแตนซ์ของ SQL Server 2017

อาจไม่ใช่เรื่องบังเอิญที่ขีด จำกัด สูงสุดที่นี่ - 32,768 - คือความจุสูงสุดของSMALLINT( Int16ใน. NET) หากเริ่มต้นที่0(ค่าสูงสุดคือ 32,767 แต่อาร์เรย์ในภาษาการเขียนโปรแกรมส่วนใหญ่ / ส่วนใหญ่เป็นแบบ 0)


0

ตอนนี้นี่เป็นเพียงหน่วยความจำไม่เพียงพอกล่าวอีกนัยหนึ่งก็คือการทำงานของโพรซีเดอร์ที่เก็บไว้ในหน่วยความจำและทรานซิสเตอร์ฮาร์ดแวร์ที่มีอยู่หรือหน่วยความจำหน้าเสมือนที่มีให้กับ SQL นั้นเต็ม!

ดังนั้นโดยทั่วไปแล้ว Stack Overflow ใน SQL Server

ตอนนี้ก่อนอื่นให้ลองทำกระบวนการให้ง่ายขึ้นเพราะเรารู้ว่าคุณต้องการตัวแปร 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

การใช้คำสั่ง SELECT แทน SET สามารถปรับปรุงประสิทธิภาพและความสามารถในการอ่านและอาจทำให้คุณได้รับข้อผิดพลาดที่ระบุไว้ ดังนั้นแทนที่จะ:

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

คุณทำได้:

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

และตั้งค่าทั้งสามค่าในคำสั่งเดียว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.