Інші відповіді досить добре висвітлюють відмінності синтаксису, тому я не буду в цьому займатися. Натомість ця відповідь буде просто охоплювати продуктивність в Oracle.
Оптимізатор Oracle може вирішити матеріалізувати результати CTE у внутрішню тимчасову таблицю. Для цього використовується евристика, а не оптимізація на основі витрат. Евристика - це щось на кшталт "Матеріалізуйте CTE, якщо це не тривіальне вираження, і CTE посилається не раз на запит". Є деякі запити, за якими матеріалізація підвищить продуктивність. Є деякі запити, за якими матеріалізація різко погіршить продуктивність. Наступний приклад трохи надуманий, але він добре ілюструє точку:
Спочатку створіть таблицю з первинним ключем, що містить цілі числа від 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;
Розглянемо наступний запит, який використовує дві похідні таблиці:
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;
Ми можемо переглянути цей запит і швидко визначити, що він не поверне жодних рядків. Oracle повинен мати можливість використовувати індекс і для визначення цього. На моїй машині запит закінчується майже миттєво із наступним планом:
Мені не подобається повторюватись, тому давайте спробуємо той самий запит із 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;
Ось план:
Це дійсно поганий план. Замість використання індексу Oracle матеріалізує 10000 X 10000 = 100000000 рядків у темп-таблицю, щоб з часом повернути 0 рядків. Вартість цього плану становить близько 6 М, що набагато вище, ніж інші запити. Для завершення запиту на моїй машині знадобилося 68 секунд.
Зауважте, що запит міг не вдатися, якщо в області таблиць темп не вистачає пам'яті або вільного місця.
Я можу використовувати незадокументований INLINE
підказку, щоб заборонити оптимізатору реалізувати 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;
Цей запит може використовувати індекс і закінчується майже миттєво. Вартість запиту така ж, як і раніше, 11. Отже, для другого запиту евристика, що використовується Oracle, призвела до того, що він вибирав запит орієнтовною вартістю 6 М замість запиту з оціночною вартістю 11.
WITH...
). Ви можете переписати кожну отриману таблицю як CTE, але, можливо, не навпаки (наприклад, рекурсивний CTE або використання CTE кілька разів)