Số lượng biến cục bộ tối đa có thể tham gia vào hoạt động SET là bao nhiêu?


11

Tôi có một thủ tục lưu trữ có chứa logic kinh doanh. Bên trong nó có khoảng 1609 biến số (đừng hỏi tôi tại sao, đây là cách động cơ hoạt động). Tôi cố gắng để SETmột biến cho giá trị nối của tất cả các biến khác. Kết quả trong quá trình tạo tôi gặp lỗi:

Msg 8631, Cấp 17, Trạng thái 1, Quy trình XXX, Dòng YYY Lỗi nội bộ: Đã đạt đến giới hạn ngăn xếp máy chủ. Vui lòng tìm kiếm khả năng lồng sâu trong truy vấn của bạn và cố gắng đơn giản hóa nó.

Tôi đã tìm ra rằng lỗi là do số lượng biến mà tôi cần sử dụng trong SEThoạt động. Tôi có thể thực hiện nhiệm vụ bằng cách chia nó làm hai.

Câu hỏi của tôi là có một số hạn chế trong lĩnh vực này? Tôi đã kiểm tra, nhưng tôi không tìm thấy.

Chúng tôi đã kiểm tra lỗi được mô tả trong KB này , nhưng đây không phải là trường hợp của chúng tôi. Chúng tôi không sử dụng bất kỳ CASEbiểu thức nào trong mã của chúng tôi. Chúng tôi sử dụng biến tạm thời đó để chuẩn bị một danh sách các giá trị phải được thay thế bằng hàm CLR. Chúng tôi đã cập nhật SQL Server của chúng tôi lên SP3 CU6 (cập nhật mới nhất), nhưng chúng tôi vẫn gặp lỗi.

Câu trả lời:


16

Msg 8631, Cấp 17, Trạng thái 1, Dòng xxx
Lỗi nội bộ: Đã đạt đến giới hạn ngăn xếp máy chủ.
Vui lòng tìm kiếm khả năng lồng sâu trong truy vấn của bạn và cố gắng đơn giản hóa nó.

Lỗi này xảy ra với các danh sách nối ghép gán dài SEThoặc SELECTbiến do cách SQL Server phân tích cú pháp và liên kết loại câu lệnh này - như một danh sách lồng ghép của hai phép nối hai đầu vào.

Ví dụ: SET @V = @W + @X + @Y + @Zbị ràng buộc vào một cây có dạng:

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

Mỗi phần tử ghép nối sau hai phần đầu tiên dẫn đến một mức độ lồng thêm vào trong biểu diễn này.

Lượng không gian ngăn xếp có sẵn cho SQL Server xác định giới hạn cuối cùng cho việc lồng nhau này. Khi vượt quá giới hạn, một ngoại lệ được đưa ra trong nội bộ, cuối cùng dẫn đến thông báo lỗi hiển thị ở trên. Ví dụ về ngăn xếp cuộc gọi khi lỗi được đưa ra được hiển thị bên dưới:

Dấu vết ngăn xếp

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

Đây là một giới hạn cơ bản do cách xử lý nhiều kết nối bên trong. Nó ảnh hưởng SETSELECTcác câu lệnh gán biến như nhau.

Cách giải quyết là giới hạn số lượng ghép được thực hiện trong một câu lệnh. Điều này cũng thường sẽ hiệu quả hơn, vì việc biên dịch cây truy vấn sâu đòi hỏi nhiều tài nguyên.


5

Lấy cảm hứng từ @ Paul 's câu trả lời , tôi đã làm một số nghiên cứu và phát hiện ra rằng trong khi nó là sự thật rằng ngăn xếp không gian không giới hạn số lượng concatenations, rằng ngăn xếp không gian là một chức năng của bộ nhớ có sẵn và do đó thay đổi, hai điểm sau đây cũng là đúng sự thật :

  1. có một cách để nhồi nhét các phép nối bổ sung vào một câu lệnh AND
  2. sử dụng phương pháp này để vượt ra ngoài giới hạn không gian ngăn xếp ban đầu, có thể tìm thấy giới hạn logic thực tế (không có vẻ thay đổi)

Đầu tiên, tôi điều chỉnh mã kiểm tra của Paul để nối chuỗi:

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

Với thử nghiệm này, mức cao nhất tôi có thể nhận được khi chạy trên máy tính xách tay không quá lớn của mình (chỉ có 6 GB RAM) là:

  • 3311 (trả về 3312 tổng số ký tự) bằng SQL Server 2017 Express Edition LocalDB (14.0.3006)
  • 3512 (trả về 3513 tổng số ký tự) bằng SQL Server 2012 Developer Edition SP4 (KB4018073) (11.0.7001)

trước khi gặp lỗi 8631 .

Tiếp theo, tôi đã thử nhóm các phép nối bằng cách sử dụng dấu ngoặc đơn sao cho thao tác sẽ được nối nhiều nhóm nối. Ví dụ:

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

Làm điều đó tôi đã có thể vượt xa các giới hạn trước đó của 3312 và 3513 biến. Mã cập nhật là:

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

Các giá trị tối đa (đối với tôi) bây giờ là sử dụng 42cho lần đầu tiên REPLICATE, do đó sử dụng 43 biến cho mỗi nhóm, sau đó sử dụng 762cho biến thứ hai REPLICATE, do đó sử dụng 762 nhóm gồm 43 biến. Nhóm ban đầu được mã hóa cứng với hai biến.

Đầu ra bây giờ cho thấy có 32.768 ký tự trong @Sbiến. Nếu tôi cập nhật nhóm ban đầu thành (@A+@A+@A)thay vì chỉ (@A+@A), thì tôi gặp lỗi sau:

Msg 8632, Cấp 17, Trạng thái 2, Dòng XXXXX
Lỗi nội bộ: Đã đạt đến giới hạn dịch vụ biểu thức. Vui lòng tìm các biểu thức có khả năng phức tạp trong truy vấn của bạn và cố gắng đơn giản hóa chúng.

Lưu ý rằng số lỗi khác với trước đây. Bây giờ là: 8632 . VÀ, tôi có cùng giới hạn này cho dù tôi sử dụng phiên bản SQL Server 2012 hoặc phiên bản SQL Server 2017.

Có lẽ không phải ngẫu nhiên mà giới hạn trên ở đây - 32.768 - là dung lượng tối đa của SMALLINT( Int16trong .NET) NẾU bắt đầu từ 0(giá trị tối đa là 32.767 nhưng mảng trong nhiều / hầu hết các ngôn ngữ lập trình đều dựa trên 0).


0

Bây giờ, điều này chỉ đơn giản là hết bộ nhớ, vì hoạt động của thủ tục lưu trữ được thực hiện trong bộ nhớ và các bóng bán dẫn phần cứng có sẵn hoặc Bộ nhớ trang ảo có sẵn cho SQL đã đầy!

Vì vậy, về cơ bản Stack Overflow của nó trong SQL Server.

Bây giờ, trước tiên hãy thử đơn giản hóa quy trình, vì chúng tôi biết bạn cần 1609 Biến,

Nhưng bạn có cần tất cả các biến cùng một lúc không?

Chúng ta có thể khai báo và sử dụng các biến khi cần thiết.

Ví dụ:

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

Nhưng nếu chúng ta thử điều này trong một vòng lặp bằng cách thêm

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

Lưu ý: Điều này sẽ sử dụng nhiều CPU hơn và mất nhiều thời gian hơn trong tính toán.

Bây giờ điều này sẽ chậm, nhưng có lợi thế của việc sử dụng bộ nhớ ít hơn.

Tôi hy vọng điều này có ích, Vui lòng gửi truy vấn của bạn để chúng tôi có thể hiểu chính xác kịch bản.


-4

Sử dụng các câu lệnh CHỌN thay vì SET có thể cải thiện hiệu suất và khả năng đọc và có thể giúp bạn khắc phục lỗi đã nêu. Vì vậy, thay vì:

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

Bạn có thể làm:

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

Và đặt cả ba giá trị trong một câu lệnh.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.