他の回答は構文の違いをかなりうまくカバーしているので、ここでは説明しません。代わりに、この回答は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;
2つの派生テーブルを使用する次のクエリについて考えます。
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行を返します。このプランのコストは約600万で、他のクエリよりもはるかに高くなります。クエリが私のマシンで完了するまでに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。2番目のクエリでは、Oracleが使用したヒューリスティックにより、推定コストが11のクエリではなく、推定コストが6 Mのクエリが選択されました。
WITH...
)です。すべての派生テーブルをCTEとして書き換えることができますが、逆の方法ではない可能性があります(たとえば、再帰的CTEまたはCTEを複数回使用)