La descripción de BOL de los CTE recursivos describe la semántica de la ejecución recursiva de la siguiente manera:
- Divide la expresión CTE en miembros ancla y recursivos.
- Ejecute los miembros de anclaje que crean la primera invocación o el conjunto de resultados base (T0).
- Ejecute los miembros recursivos con Ti como entrada y Ti + 1 como salida.
- Repita el paso 3 hasta que se devuelva un conjunto vacío.
- Devuelve el conjunto de resultados. Esta es una UNIÓN TODO de T0 a Tn.
Tenga en cuenta que lo anterior es una descripción lógica . El orden físico de las operaciones puede ser algo diferente, como se ilustra aquí.
Aplicando esto a su CTE, esperaría un bucle infinito con el siguiente patrón
+-----------+---------+---+---+---+
| 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)
es la expresión de anclaje. Esto claramente regresa 1,2,3
comoT0
A partir de entonces se ejecuta la expresión recursiva
select a
from cte
except
select a
from r
Con una 1,2,3
entrada como que producirá una salida de 4,5
as T1
, volverá a enchufarla para la próxima ronda de recursión 1,2,3
y así continuará indefinidamente.
Sin embargo, esto no es lo que realmente sucede. Estos son los resultados de las primeras 5 invocaciones.
+-----------+---------+---+---+---+
| 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 |
+-----------+---------+---+---+---+
Al usar OPTION (MAXRECURSION 1)
y ajustar hacia arriba en incrementos, 1
se puede ver que entra en un ciclo donde cada nivel sucesivo alternará continuamente entre la salida 1,2,3,4
y 1,2,3,5
.
Como lo discutió @Quassnoi en esta publicación de blog . El patrón de resultados observados es como si cada invocación estuviera haciendo (1),(2),(3),(4),(5) EXCEPT (X)
dónde X
está la última fila de la invocación anterior.
Editar: Después de leer la excelente respuesta de SQL Kiwi, está claro por qué ocurre esto y que esta no es toda la historia, ya que todavía hay un montón de cosas en la pila que nunca se pueden procesar.
El ancla se emite 1,2,3
al contenido de la pila del cliente3,2,1
3 saltaron de la pila, Contenido de la pila 2,1
El LASJ regresa 1,2,4,5
, Contenido de la pila5,4,2,1,2,1
5 saltaron de la pila, Contenido de la pila 4,2,1,2,1
El LASJ devuelve 1,2,3,4
Contenido de la pila4,3,2,1,5,4,2,1,2,1
4 saltaron de la pila, contenido de la pila 3,2,1,5,4,2,1,2,1
El LASJ devuelve 1,2,3,5
Contenido de la pila5,3,2,1,3,2,1,5,4,2,1,2,1
5 saltaron de la pila, Contenido de la pila 3,2,1,3,2,1,5,4,2,1,2,1
El LASJ devuelve 1,2,3,4
Contenido de la pila
4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1
Si intenta reemplazar el miembro recursivo con la expresión lógicamente equivalente (en ausencia de duplicados / NULL)
select a
from (
select a
from cte
where a not in
(select a
from r)
) x
Esto no está permitido y genera el error "No se permiten referencias recursivas en subconsultas". entonces quizás es un descuido que EXCEPT
incluso se permite en este caso.
Adición:
Microsoft ha respondido a mis comentarios de Connect como se muestra a continuación
La suposición de Jack es correcta: esto debería haber sido un error de sintaxis; las referencias recursivas no deberían permitirse en las EXCEPT
cláusulas. Planeamos abordar este error en una próxima versión del servicio. Mientras tanto, sugeriría evitar referencias recursivas en EXCEPT
cláusulas.
Al restringir la recursión EXCEPT
, seguimos el estándar ANSI SQL, que ha incluido esta restricción desde que se introdujo la recursividad (en 1999, creo). No existe un acuerdo generalizado sobre cuál debería ser la semántica para la recursión EXCEPT
(también llamada "negación no estratificada") en lenguajes declarativos como SQL. Además, es notoriamente difícil (si no imposible) implementar dicha semántica de manera eficiente (para bases de datos de tamaño razonable) en un sistema RDBMS.
Y parece que la implementación final se realizó en 2014 para bases de datos con un nivel de compatibilidad de 120 o superior .
Las referencias recursivas en una cláusula EXCEPT generan un error de conformidad con el estándar ANSI SQL.