La description BOL des CTE récursifs décrit la sémantique de l'exécution récursive comme suit:
- Divisez l'expression CTE en membres d'ancrage et récursifs.
- Exécutez le ou les membres d'ancrage créant le premier jeu de résultats d'invocation ou de base (T0).
- Exécutez le ou les membres récursifs avec Ti en entrée et Ti + 1 en sortie.
- Répétez l'étape 3 jusqu'à ce qu'un jeu vide soit renvoyé.
- Renvoie le jeu de résultats. Ceci est une UNION ALL de T0 à Tn.
Notez que ce qui précède est une description logique . L'ordre physique des opérations peut être quelque peu différent, comme illustré ici.
En appliquant cela à votre CTE, je m'attendrais à une boucle infinie avec le motif suivant
+-----------+---------+---+---+---+
| Invocation| Results |
+-----------+---------+---+---+---+
| 1 | 1 | 2 | 3 | |
| 2 | 4 | 5 | | |
| 3 | 1 | 2 | 3 | |
| 4 | 4 | 5 | | |
| 5 | 1 | 2 | 3 | |
+-----------+---------+---+---+---+
Car
select a
from cte
where a in (1,2,3)
est l'expression d'ancrage. Ceci retourne clairement 1,2,3
commeT0
Ensuite, l'expression récursive s'exécute
select a
from cte
except
select a
from r
Avec 1,2,3
comme entrée qui produira un signal de sortie 4,5
comme T1
puis de brancher ce avant pour le prochain cycle de récurrence sera de retour 1,2,3
et ainsi de suite indéfiniment.
Ce n'est pas ce qui se passe réellement cependant. Ce sont les résultats des 5 premières invocations
+-----------+---------+---+---+---+
| 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 |
+-----------+---------+---+---+---+
En utilisant OPTION (MAXRECURSION 1)
et en augmentant progressivement les incréments, 1
on peut voir qu’il entre dans un cycle où chaque niveau successif bascule continuellement entre les sorties 1,2,3,4
et 1,2,3,5
.
Comme discuté par @Quassnoi dans cet article de blog . Le modèle des résultats observés est comme si chaque appel se déroulait, (1),(2),(3),(4),(5) EXCEPT (X)
où X
se trouvait la dernière ligne de l'appel précédent.
Edit: Après avoir lu l'excellente réponse de SQL Kiwi, il est clair à la fois pourquoi cela se produit et qu'il ne s'agit pas là de toute l'histoire, car il reste encore beaucoup de choses sur la pile qui ne peuvent jamais être traitées.
Ancre émet 1,2,3
au contenu de la pile du client3,2,1
3 piles sautées, contenu de la pile 2,1
Le LASJ revient 1,2,4,5
, empile le contenu5,4,2,1,2,1
5 piles sautées, contenu de la pile 4,2,1,2,1
Le LASJ renvoie le 1,2,3,4
contenu de la pile4,3,2,1,5,4,2,1,2,1
4 sauté hors pile, contenu de la pile 3,2,1,5,4,2,1,2,1
Le LASJ renvoie le 1,2,3,5
contenu de la pile5,3,2,1,3,2,1,5,4,2,1,2,1
5 piles sautées, contenu de la pile 3,2,1,3,2,1,5,4,2,1,2,1
Le LASJ renvoie le 1,2,3,4
contenu de la pile
4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1
Si vous essayez de remplacer le membre récursif par l'expression logiquement équivalente (en l'absence de doublons / NULL)
select a
from (
select a
from cte
where a not in
(select a
from r)
) x
Ceci n'est pas autorisé et génère l'erreur "Les références récursives ne sont pas autorisées dans les sous-requêtes." alors c'est peut-être un oubli qui EXCEPT
est même permis dans ce cas.
Ajout:
Microsoft a maintenant répondu à mes commentaires sur Connect, comme indiqué ci-dessous.
La supposition de Jack est correcte: cela aurait dû être une erreur de syntaxe; les références récursives ne devraient en effet pas être autorisées dans les EXCEPT
clauses. Nous prévoyons de résoudre ce problème dans une prochaine version du service. Entre-temps, je suggérerais d'éviter les références récursives dans les EXCEPT
clauses.
En limitant la récursivité, EXCEPT
nous respectons le standard ANSI SQL, qui inclut cette restriction depuis l’introduction de la récursivité (en 1999, je crois). Il n'y a pas d'accord général sur ce que la sémantique devrait être pour la récursion EXCEPT
(également appelée "négation non stratifiée") dans des langages déclaratifs tels que SQL. En outre, il est notoirement difficile (voire impossible) de mettre en œuvre efficacement une telle sémantique (pour des bases de données de taille raisonnable) dans un système de SGBDR.
Et on dirait que la mise en œuvre éventuelle a été réalisée en 2014 pour les bases de données avec un niveau de compatibilité de 120 ou plus .
Les références récursives dans une clause EXCEPT génèrent une erreur conforme au standard SQL ANSI.