재귀 CTE에 대한 BOL 설명은 재귀 실행의 의미를 다음과 같이 설명합니다.
- CTE 표현식을 앵커 및 재귀 멤버로 분할하십시오.
- 첫 번째 호출 또는 기본 결과 세트 (T0)를 작성하는 앵커 멤버를 실행하십시오.
- Ti를 입력으로하고 Ti + 1을 출력으로하여 재귀 멤버를 실행합니다.
- 빈 세트가 반환 될 때까지 3 단계를 반복하십시오.
- 결과 집합을 반환합니다. 이것은 T0에서 Tn까지의 UNION ALL입니다.
위의 내용은 논리적 설명입니다. 실제 작업 순서는 여기에 설명 된대로 약간 다를 수 있습니다.
이것을 CTE에 적용하면 다음 패턴의 무한 루프가 예상됩니다.
+-----------+---------+---+---+---+
| Invocation| Results |
+-----------+---------+---+---+---+
| 1 | 1 | 2 | 3 | |
| 2 | 4 | 5 | | |
| 3 | 1 | 2 | 3 | |
| 4 | 4 | 5 | | |
| 5 | 1 | 2 | 3 | |
+-----------+---------+---+---+---+
때문에
select a
from cte
where a in (1,2,3)
앵커 식입니다. 이 분명히 반환 1,2,3
로T0
그 후 재귀 표현식이 실행됩니다.
select a
from cte
except
select a
from r
으로 1,2,3
의 출력을 보장 할뿐만 입력 4,5
으로서 T1
다음 반환 재귀의 다음 라운드에서 그 뒷면을 연결 1,2,3
등 무기한.
그러나 이것은 실제로 일어나지 않습니다. 처음 5 번의 호출 결과입니다
+-----------+---------+---+---+---+
| Invocation| Results |
+-----------+---------+---+---+---+
| 1 | 1 | 2 | 3 | |
| 2 | 1 | 2 | 4 | 5 |
| 3 | 1 | 2 | 3 | 4 |
| 4 | 1 | 2 | 3 | 5 |
| 5 | 1 | 2 | 3 | 4 |
+-----------+---------+---+---+---+
사용 OPTION (MAXRECURSION 1)
및 상향 조정을 1
통해 각 연속 레벨이 지속적으로 출력 1,2,3,4
과 사이를 전환하는 사이클에 진입 함 을 알 수 있습니다 1,2,3,5
.
이 블로그 게시물 에서 @Quassnoi 가 논의한대로 . 관찰 된 결과의 패턴은 각 호출이 이전 호출의 마지막 행 이있는 곳 과 같습니다.(1),(2),(3),(4),(5) EXCEPT (X)
X
편집 : SQL Kiwi의 훌륭한 대답을 읽은 후에 는 이것이 왜 발생하는지, 그리고 처리 할 수없는 스택에 여전히 많은 양의 물건이 남아 있다는 점에서 이것이 전체 이야기가 아님을 분명히 알 수 있습니다.
앵커 1,2,3
는 클라이언트 스택 컨텐츠로 방출 됩니다.3,2,1
스택에서 3이 튀어 나옴, 스택 내용 2,1
LASJ가 1,2,4,5
스택 내용을 반환합니다 .5,4,2,1,2,1
스택에서 5 개가 튀어 나옴, 스택 내용 4,2,1,2,1
LASJ는 1,2,3,4
스택 내용을 반환4,3,2,1,5,4,2,1,2,1
스택 4 개가 튀어 나옴, 스택 내용 3,2,1,5,4,2,1,2,1
LASJ는 1,2,3,5
스택 내용을 반환5,3,2,1,3,2,1,5,4,2,1,2,1
스택에서 5 개가 튀어 나옴, 스택 내용 3,2,1,3,2,1,5,4,2,1,2,1
LASJ는 1,2,3,4
스택 내용을
반환4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1
재귀 멤버를 논리적으로 동등한 (중복 / NULL이없는 경우) 식으로 바꾸려고하면
select a
from (
select a
from cte
where a not in
(select a
from r)
) x
이것은 허용되지 않으며 "하위 쿼리에서 재귀 참조는 허용되지 않습니다"라는 오류를 발생시킵니다. 따라서이 EXCEPT
경우에도 허용 되는 것은 감독입니다 .
추가 :
Microsoft는 이제 다음과 같이 내 연결 피드백에 응답했습니다.
Jack 의 추측은 맞습니다. 이것은 구문 오류 였을 것입니다. 재귀 참조는 실제로 EXCEPT
절 에서 허용되지 않아야 합니다. 향후 서비스 릴리스에서이 버그를 해결할 계획입니다. 한편, EXCEPT
절 에서 재귀 참조를 피하는 것이 좋습니다 .
재귀를 제한 할 EXCEPT
때 우리는 재귀가 도입 된 이후 (1999 년에 믿었던) ANSI SQL 표준을 따릅니다. EXCEPT
SQL과 같은 선언적 언어에서 재귀를 위해 의미론이 무엇이어야하는지 ( "계층화되지 않은 부정"이라고도 함)에 대한 광범위한 동의는 없습니다 . 또한 RDBMS 시스템에서 이러한 의미를 효율적으로 (합리적인 크기의 데이터베이스에) 구현하는 것은 매우 어렵습니다 (불가능하지 않은 경우).
호환성 수준이 120 이상인 데이터베이스 에 대해 2014 년에 최종 구현 된 것처럼 보입니다 .
EXCEPT 절의 재귀 참조는 ANSI SQL 표준을 준수하는 오류를 생성합니다.