Другие ответы достаточно хорошо охватывают синтаксические различия, поэтому я не буду вдаваться в подробности. Вместо этого этот ответ будет охватывать только производительность в 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 секунд.
Обратите внимание, что запрос мог завершиться неудачно, если в табличном пространстве temp недостаточно памяти или свободного места.
Я могу использовать недокументированную 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)