Inne odpowiedzi całkiem dobrze pokrywają różnice w składni, więc nie będę w to wchodził. Zamiast tego ta odpowiedź dotyczy tylko wydajności Oracle.
Optymalizator Oracle może zmaterializować wyniki CTE w wewnętrznej tabeli tymczasowej. Używa do tego heurystyki zamiast optymalizacji opartej na kosztach. Heurystyka jest czymś w rodzaju „Zmaterializuj CTE, jeśli nie jest to trywialne wyrażenie, a do CTE odwołuje się więcej niż raz w zapytaniu”. Istnieje kilka zapytań, dla których materializacja poprawi wydajność. Istnieje kilka zapytań, w przypadku których materializacja radykalnie obniży wydajność. Poniższy przykład jest nieco wymyślony, ale dobrze ilustruje ten punkt:
Najpierw utwórz tabelę z kluczem podstawowym zawierającym liczby całkowite od 1 do 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;
Rozważ następujące zapytanie, które korzysta z dwóch tabel pochodnych:
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;
Możemy spojrzeć na to zapytanie i szybko ustalić, że nie zwróci on żadnych wierszy. Oracle powinna być w stanie również użyć tego indeksu do ustalenia tego. Na moim komputerze zapytanie kończy się niemal natychmiast z następującym planem:
Nie lubię się powtarzać, więc spróbujmy tego samego zapytania z 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;
Oto plan:
To naprawdę zły plan. Zamiast używać indeksu, Oracle materializuje 10000 X 10000 = 100000000 wierszy do tabeli tymczasowej tylko po to, by ostatecznie zwrócić 0 rzędów. Koszt tego planu wynosi około 6 M, co jest znacznie wyższe niż w przypadku innego zapytania. Zapytanie zajęło 68 sekund, aby zakończyć na moim komputerze.
Zwróć uwagę, że zapytanie mogło się nie powieść, jeśli nie ma wystarczającej ilości pamięci lub wolnego miejsca w tymczasowym obszarze tabel.
Mogę użyć nieudokumentowanej INLINE
wskazówki, aby uniemożliwić optymalizatorowi zmaterializowanie 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;
To zapytanie może korzystać z indeksu i kończy się niemal natychmiast. Koszt zapytania jest taki sam jak poprzednio, 11. Zatem w przypadku drugiego zapytania heurystyka zastosowana przez Oracle spowodowała, że wybrał zapytanie o szacowanym koszcie 6 M zamiast zapytania o szacowanym koszcie 11.
WITH...
). Możesz przepisać każdą tabelę pochodną jako CTE, ale może nie na odwrót (np. Rekurencyjne CTE lub wielokrotne używanie CTE)