A descrição da BOL de CTEs recursivas descreve a semântica da execução recursiva da seguinte maneira:
- Divida a expressão CTE em membros âncora e recursivos.
- Execute o (s) membro (s) âncora (s) criando a primeira chamada ou conjunto de resultados base (T0).
- Execute os membros recursivos com Ti como entrada e Ti + 1 como saída.
- Repita a etapa 3 até que um conjunto vazio seja retornado.
- Retorne o conjunto de resultados. Este é um UNION ALL de T0 a Tn.
Observe o acima é uma descrição lógica .A ordem física das operações pode ser um pouco diferente, como ilustrado aqui
Aplicando isso ao seu CTE, eu esperaria um loop infinito com o seguinte padrão
+-----------+---------+---+---+---+
| Invocation| Results |
+-----------+---------+---+---+---+
| 1 | 1 | 2 | 3 | |
| 2 | 4 | 5 | | |
| 3 | 1 | 2 | 3 | |
| 4 | 4 | 5 | | |
| 5 | 1 | 2 | 3 | |
+-----------+---------+---+---+---+
Porque
select a
from cte
where a in (1,2,3)
é a expressão âncora. Isso claramente retorna 1,2,3
comoT0
Depois disso, a expressão recursiva é executada
select a
from cte
except
select a
from r
Com a 1,2,3
entrada que produzirá uma saída da 4,5
mesma forma T1
que conectá-la novamente para a próxima rodada de recursão retornará 1,2,3
e assim por diante indefinidamente.
Isto não é o que realmente acontece no entanto. Estes são os resultados das 5 primeiras invocações
+-----------+---------+---+---+---+
| 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 |
+-----------+---------+---+---+---+
Usando OPTION (MAXRECURSION 1)
e ajustando para cima em incrementos 1
, pode-se ver que ele entra em um ciclo em que cada nível sucessivo alterna continuamente entre a saída 1,2,3,4
e 1,2,3,5
.
Como discutido por @Quassnoi em este post . O padrão dos resultados observados é como se cada chamada estivesse fazendo (1),(2),(3),(4),(5) EXCEPT (X)
onde X
está a última linha da chamada anterior.
Edit: Depois de ler a excelente resposta do SQL Kiwi , fica claro por que isso ocorre e que essa não é a história toda, pois ainda há muitas coisas na pilha que nunca podem ser processadas.
A âncora é emitida 1,2,3
para o conteúdo da pilha do cliente3,2,1
3 saiu da pilha, Conteúdo da pilha 2,1
O LASJ retorna 1,2,4,5
, Conteúdo da pilha5,4,2,1,2,1
5 saiu da pilha, Conteúdo da pilha 4,2,1,2,1
O LASJ retorna o 1,2,3,4
Conteúdo da pilha4,3,2,1,5,4,2,1,2,1
4 saiu da pilha, Conteúdo da pilha 3,2,1,5,4,2,1,2,1
O LASJ retorna o 1,2,3,5
Conteúdo da pilha5,3,2,1,3,2,1,5,4,2,1,2,1
5 saiu da pilha, Conteúdo da pilha 3,2,1,3,2,1,5,4,2,1,2,1
O LASJ retorna o 1,2,3,4
Conteúdo da pilha
4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1
Se você tentar substituir o membro recursivo pela expressão logicamente equivalente (na ausência de duplicatas / NULLs)
select a
from (
select a
from cte
where a not in
(select a
from r)
) x
Isso não é permitido e gera o erro "Referências recursivas não são permitidas em subconsultas". então talvez seja uma supervisão que EXCEPT
seja permitida neste caso.
Adição: a
Microsoft agora respondeu aos meus comentários do Connect, conforme abaixo
O palpite de Jack está correto: deveria ter sido um erro de sintaxe; referências recursivas não devem, de fato, ser permitidas em EXCEPT
cláusulas. Planejamos solucionar esse bug em um próximo release de serviço. Enquanto isso, sugiro evitar referências recursivas nas EXCEPT
cláusulas.
Ao restringir a recursão EXCEPT
, seguimos o padrão ANSI SQL, que inclui essa restrição desde que a recursão foi introduzida (em 1999, acredito). Não há um consenso generalizado sobre o que a semântica deve ser para recursão EXCEPT
(também chamada de "negação não estratificada") em linguagens declarativas como SQL. Além disso, é notoriamente difícil (se não impossível) implementar essa semântica de maneira eficiente (para bancos de dados de tamanho razoável) em um sistema RDBMS.
E parece que a eventual implementação foi feita em 2014 para bancos de dados com nível de compatibilidade de 120 ou superior .
Referências recursivas em uma cláusula EXCEPT geram um erro de conformidade com o padrão ANSI SQL.