D'autres réponses couvrent assez bien les différences de syntaxe, donc je n'entrerai pas dans les détails. Au lieu de cela, cette réponse couvrira simplement les performances dans Oracle.
L'optimiseur Oracle peut choisir de matérialiser les résultats d'un CTE dans une table temporaire interne. Il utilise une heuristique pour ce faire au lieu d'une optimisation basée sur les coûts. L'heuristique est quelque chose comme "Matérialiser le CTE s'il n'est pas une expression triviale et le CTE est référencé plus d'une fois dans la requête". Il existe certaines requêtes pour lesquelles la matérialisation améliorera les performances. Il existe certaines requêtes pour lesquelles la matérialisation dégradera considérablement les performances. L'exemple suivant est un peu artificiel mais il illustre bien le point:
Créez d'abord une table avec une clé primaire qui contient des entiers de 1 à 10000:
CREATE TABLE N_10000 (NUM_ID INTEGER NOT NULL, PRIMARY KEY (NUM_ID));
INSERT /*+APPEND */ INTO N_10000
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 10000
ORDER BY LEVEL;
COMMIT;
Considérez la requête suivante qui utilise deux tables dérivées:
SELECT t1.NUM_ID
FROM
(
SELECT n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
) t1
LEFT OUTER JOIN
(
SELECT n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
) t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;
Nous pouvons examiner cette requête et déterminer rapidement qu'elle ne renverra aucune ligne. Oracle devrait également pouvoir utiliser l'index pour le déterminer. Sur ma machine, la requête se termine presque instantanément avec le plan suivant:
Je n'aime pas me répéter, alors essayons la même requête avec un CTE:
WITH N_10000_CTE AS (
SELECT n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;
Voici le plan:
C'est vraiment un mauvais plan. Au lieu d'utiliser l'index, Oracle matérialise 10000 X 10000 = 100000000 lignes dans une table temporaire pour finalement retourner 0 lignes. Le coût de ce plan est d'environ 6 M, ce qui est beaucoup plus élevé que l'autre requête. La requête a pris 68 secondes pour se terminer sur ma machine.
Notez que la requête peut avoir échoué s'il n'y a pas suffisamment de mémoire ou d'espace libre dans l'espace disque logique temporaire.
Je peux utiliser l' INLINE
indice non documenté pour empêcher l'optimiseur de matérialiser le CTE:
WITH N_10000_CTE AS (
SELECT /*+ INLINE */ n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;
Cette requête est capable d'utiliser l'index et se termine presque instantanément. Le coût de la requête est le même qu'avant, 11. Donc pour la deuxième requête, l'heuristique utilisée par Oracle l'a amené à choisir une requête avec un coût estimé à 6 M au lieu d'une requête avec un coût estimé à 11.
WITH...
). Vous pouvez réécrire chaque table dérivée en CTE, mais peut-être pas l'inverse (par exemple, CTE récursif ou utiliser le CTE plusieurs fois)