SET 연산에 참여할 수있는 최대 로컬 변수 수는 얼마입니까?


11

비즈니스 논리가 포함 된 저장 프로 시저가 있습니다. 내부에는 약 1609 개의 변수가 있습니다 (이것이 엔진 작동 방식을 묻지 마십시오). SET변수를 다른 모든 변수의 연결된 값으로 시도 합니다. 결과적으로 생성 중에 오류가 발생합니다.

메시지 8631, 수준 17, 상태 1, 절차 XXX, 줄 YYY 내부 오류 : 서버 스택 제한에 도달했습니다. 쿼리에서 잠재적으로 깊은 중첩을 찾아서 단순화하십시오.

오류는 SET작업 에 사용해야하는 변수의 수로 인한 것임을 알았습니다 . 과제를 둘로 나눠서 과제를 수행 할 수 있습니다.

내 질문은이 영역에 제한이 있습니까? 확인했지만 찾지 못했습니다.

이 KB에 설명 된 오류를 확인 했지만 우리의 경우는 아닙니다. 우리는 CASE코드 내에서 표현식을 사용하지 않습니다 . 이 임시 변수를 사용하여 CLR 함수를 사용하여 대체해야하는 값 목록을 준비합니다. SQL Server를 SP3 CU6 (최신)으로 업데이트했지만 여전히 오류가 발생합니다.

답변:


16

메시지 8631, 수준 17, 상태 1, 줄 xxx
내부 오류 : 서버 스택 제한에 도달했습니다.
쿼리에서 잠재적으로 깊은 중첩을 찾아서 단순화하십시오.

이 오류는 긴 발생 SET또는 SELECT방식 때문에 SQL 서버 구문 분석에 변수 할당 연결 목록 및 문이 유형의 결합 - 두 개의 입력 연결로 중첩 된 목록으로합니다.

예를 들어, 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에서 사용할 수있는 스택 공간의 양에 따라이 중첩에 대한 최종 제한이 결정됩니다. 제한이 초과되면 내부적으로 예외가 발생하여 결국 위에 표시된 오류 메시지가 나타납니다. 오류가 발생할 때 프로세스 호출 스택의 예는 다음과 같습니다.

스택 추적

재현

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

이는 여러 연결이 내부적으로 처리되는 방식으로 인한 근본적인 한계입니다. 그것은 영향을 미치는 SETSELECT동일하게 변수 할당 문.

임시 해결책은 단일 명령문에서 수행되는 연결 수를 제한하는 것입니다. 딥 쿼리 트리를 컴파일하는 것은 리소스를 많이 사용하기 때문에 일반적으로 더 효율적입니다.


5

에서 영감 @ 폴대답은 , 제가 조사를 좀 해봤 발견은 스택 공간이 제한에게 회씩 연결의 수를 않습니다, 것은 사실이지만 그 스택 공간이 사용 가능한 메모리의 함수이며, 따라서 다음 두 가지 사항도 참 다양합니다 :

  1. 추가 연결을 단일 명령문으로 작성하는 방법이 있습니다.
  2. 이 방법을 사용하여 초기 스택 공간 제한을 넘어 서면 실제 논리적 한계 (다양하게 보이지 않음)를 찾을 수 있습니다.

먼저 Paul의 테스트 코드를 조정하여 문자열을 연결했습니다.

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

이 테스트를 통해 그다지 우수하지 않은 랩톱 (6GB RAM 만)에서 실행할 때 얻을 수있는 가장 높은 값은 다음과 같습니다.

  • SQL Server 2017 Express Edition LocalDB를 사용하는 3311 (총 3312 자 반환) (14.0.3006)
  • SQL Server 2012 Developer Edition 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)just 대신 업데이트 (@A+@A)하면 다음 오류가 발생합니다.

메시지 8632, 수준 17, 상태 2, 줄 XXXXX
내부 오류 : 식 서비스 제한에 도달했습니다. 쿼리에서 잠재적으로 복잡한 표현식을 찾아서 단순화하십시오.

오류 번호가 이전과 다릅니다. 지금 : 8632 . 또한 SQL Server 2012 인스턴스를 사용하든 SQL Server 2017 인스턴스를 사용하든 동일한 제한이 있습니다.

여기서 32.768의 상한값 이 ( .NET에서) 최대 용량 인 IF 의 최대 용량 (최대 값은 32,767이지만 많은 / 대부분의 프로그래밍 언어의 배열은 0 기반) 이라는 것은 우연의 일치가 아닙니다 .SMALLINTInt160


0

즉, 메모리에서 수행되는 저장 프로 시저와 SQL에 사용할 수있는 사용 가능한 하드웨어 트랜지스터 또는 가상 페이지 메모리의 작업이 가득 찼기 때문에 이것은 단순히 메모리 부족입니다.

따라서 기본적으로 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

SET 대신 SELECT 문을 사용하면 성능과 가독성이 향상되고 명시된 오류를 해결할 수 있습니다. 따라서 대신 :

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.