이것은 프로젝트 정규화 의 버그이며 , 결정적이지 않은 함수가있는 case 표현식 내에서 하위 쿼리를 사용하여 노출됩니다.
설명하기 위해, 우리는 두 가지를 미리주의해야합니다.
- SQL Server는 하위 쿼리를 직접 실행할 수 없으므로 항상 롤을 풀거나 apply 로 변환합니다 .
- 의 의미는
CASE하도록되어 THEN경우 표현은 평가되어야한다 WHEN절은 true를 반환합니다.
문제가있는 경우에 도입 된 (사소한) 하위 쿼리는 적용 연산자 (중복 루프 조인)를 발생시킵니다. 두 번째 요구 사항을 충족하기 위해 SQL Server는 처음에 표현식 dbo.test6(1) + dbo.test6(2)을 적용의 내부에 배치합니다.

[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
... 결합에 대한 통과 술어 가 CASE의미 를 갖는 의미 :
[@i]=(1) OR [@i]=(2) OR IsFalseOrNull [@i]=(3)
루프의 내부는 통과 조건이 false (의미 @i = 3)로 평가되는 경우에만 평가됩니다 . 이것은 지금까지 모두 정확합니다. 중첩 루프 조인 뒤 의 계산 스칼라 도 CASE의미를 올바르게 존중 합니다.
[Expr1001] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
문제는 쿼리 컴파일 의 프로젝트 정규화 단계에서 Expr1000상관 관계가없는 것을보고 루프 외부로 이동하는 것이 안전하다는 것을 결정한다는 것입니다 ( 내레이터 : 그렇지 않습니다 ).

[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
이것은 pass-through 술어에 의해 구현 된 의미론을 깨뜨 리므로 * 함수는 없어야 할 때 평가되며 무한 루프 결과입니다.
이 버그를보고해야합니다. 해결 방법은 표현식이 상관 관계에 따라 (즉 @i, 표현식에 포함) 적용 외부로 이동되지 않도록하는 것 입니다. 물론 이것은 해킹입니다. 프로젝트 정규화를 비활성화하는 방법이 있지만 공개적으로 공유하지 않도록 요청 받았으므로 그렇게하지 않습니다.
인라인 논리가 구문 분석 된 트리에서 직접 작동하기 때문에 (프로젝트 정규화 이전에) 스칼라 함수가 인라인 될 때 SQL Server 2019에서이 문제가 발생하지 않습니다 . 문제의 간단한 논리는 비 재귀에 대한 인라인 논리로 단순화 할 수 있습니다.
[Expr1019] = (Scalar Operator((1)))
[Expr1045] = Scalar Operator(CONVERT_IMPLICIT(int,CONVERT_IMPLICIT(int,[Expr1019],0)+(2),0))
... 3을 반환합니다.
핵심 문제를 설명하는 또 다른 방법은 다음과 같습니다.
-- Not schema bound to make it non-det
CREATE OR ALTER FUNCTION dbo.Error()
RETURNS integer
-- WITH INLINE = OFF -- SQL Server 2019 only
AS
BEGIN
RETURN 1/0;
END;
GO
DECLARE @i integer = 1;
SELECT
CASE
WHEN @i = 1 THEN 1
WHEN @i = 2 THEN 2
WHEN @i = 3 THEN (SELECT dbo.Error()) -- 'subquery'
ELSE NULL
END;
2008 R2에서 2019 CTP 3.0까지 모든 버전의 최신 빌드를 재현합니다.
Martin Smith가 제공하는 추가 예 (스칼라 함수 없음) :
SELECT IIF(@@TRANCOUNT >= 0, 1, (SELECT CRYPT_GEN_RANDOM(4)/ 0))
여기에는 필요한 모든 핵심 요소가 있습니다.
CASE(로 내부적으로 구현 됨 ScaOp_IIF)
- 비 결정적 함수 (
CRYPT_GEN_RANDOM)
- 실행해서는 안되는 분기의 하위 쿼리 (
(SELECT ...))
* 엄격히, 위의 변환은 Expr1000안전한 구조에서만 참조되기 때문에 평가가 올바르게 연기 된 경우에도 여전히 정확할 수 있습니다 .
[Expr1002] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
...하지만 내부 ForceOrder 플래그 (쿼리 힌트 아님) 가 필요하지만 설정되지 않았습니다. 어쨌든, 프로젝트 정규화 에 의해 적용된 로직의 구현 은 부정확하거나 불완전합니다.
SQL Server에 대한 Azure 피드백 사이트의 버그 보고서