La réponse est simple.
SQL Server ne matérialise pas les CTE. Il les intègre, comme vous pouvez le voir sur les plans d'exécution.
D'autres SGBD peuvent l'implémenter différemment, un exemple bien connu est Postgres, qui matérialise les CTE (il crée essentiellement des tables temporaires pour les CTE derrière le capot).
La rapidité de la matérialisation explicite des résultats intermédiaires dans des tables temporaires explicites dépend de la requête.
Dans les requêtes complexes, la surcharge d'écriture et de lecture de données intermédiaires dans des tables temporaires peut être compensée par des plans d'exécution plus simples et plus efficaces que l'optimiseur est capable de générer.
D'un autre côté, dans Postgres, CTE est une "barrière d'optimisation" et le moteur ne peut pas pousser les prédicats à travers la limite CTE.
Parfois une façon est meilleure, parfois une autre. Une fois que la complexité de la requête dépasse un certain seuil, un optimiseur ne peut pas analyser toutes les manières possibles de traiter les données et il doit choisir quelque chose. Par exemple, l'ordre dans lequel joindre les tables. Le nombre de permutations augmente de manière exponentielle avec le nombre de tables parmi lesquelles choisir. L'optimiseur dispose d'un temps limité pour générer un plan, il peut donc faire un mauvais choix lorsque tous les CTE sont alignés. Lorsque vous décomposez manuellement une requête complexe en requêtes plus petites et plus simples, vous devez comprendre ce que vous faites, mais l'optimiseur a plus de chances de générer un bon plan pour chaque requête simple.