インラインビューとWITH句の違いは何ですか?


9

インラインビューを使用すると、サブクエリから別のテーブルのように選択できます。

SELECT
    *
FROM /* Selecting from a query instead of table */
    (
        SELECT
            c1
        FROM
            t1
        WHERE
            c1 > 0
    ) a
WHERE
    a.c1 < 50;

これは、インラインビュー、WITH句、CTE、派生テーブルなど、さまざまな用語を使用して言及されているのを見てきました。私には、同じものに対して異なるベンダー固有の構文があるようです。

これは間違った仮定ですか?これらの間に技術/パフォーマンスの違いはありますか?


5
標準SQLの「公式」名は、派生テーブル(OracleがInline Viewと命名)と共通テーブル式(= WITH...)です。すべての派生テーブルをCTEとして書き換えることができますが、逆の方法ではない可能性があります(たとえば、再帰的CTEまたはCTEを複数回使用)
dnoeth

回答:


8

Oracleのインラインビュー(派生テーブル)とWITH句(CTE)の間には、いくつかの重要な違いがあります。それらのいくつかは非常に普遍的です、すなわち他のRDBMSに適用可能です。

  1. WITH インラインビューではなく、再帰的なサブクエリを作成するために使用できます(私が知る限り、CTEをサポートするすべてのRDBMSについて同じです)
  2. WITH句のサブクエリは、最初に物理的に実行される可能性が高くなります。多くの場合、WITHインラインビューとインラインビューのどちらを選択するかによって、オプティマイザーは異なる実行プランを選択するようになります(ベンダー固有、おそらくバージョン固有だと思います)。
  3. のサブクエリはWITH一時テーブルとして具体化できます(Oracle以外のベンダーがこの機能をサポートしているかどうかはわかりません)。
  4. でサブクエリWITH他のサブクエリでは、と(真のほとんどのRDBMSのための)メインクエリで、複数回参照することができます。

MySQL(少なくとも最新のMariaDBバージョン)は、派生テーブルを具体化できます(インデックスを追加することもできます)。
ypercubeᵀᴹ

3
副次的な利点として、CTEを使用することは一般的に人間にとっても読みやすくなることを付け加えておきます。
Joishi Bodio

@JoishiBodio:個人的には同意しますが、読みやすさはかなり主観的な問題です。私はそれについて言及するのを避けたい
a1ex07

さらに、CTEは以前に宣言されたCTEを参照できます。派生テーブルLATERALは、使用されない限り、同じレベルで以前に宣言された派生テーブルを参照できません。
Lennart、

8

他の回答は構文の違いをかなりうまくカバーしているので、ここでは説明しません。代わりに、この回答は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のクエリが選択されました。


1

SQL Serverの場合WITH CTE、一時的な名前付き結果セットを指定しますが、最初のにのみ必要ですCTE。すなわち

WITH CTE AS (SELECT .... FROM), 
CTE2 AS (SELECT .... FROM)

SELECT CTE.Column, CTE2.Column
FROM CTE
INNER JOIN CTE2 on CTE.Column = CTE2.Column

しかし、これはサブクエリや相関サブクエリではありません。CTEで参照できるテーブルの更新など、SQL Serverのサブクエリでは実行できないことをCTEで実行できます。これは、 CTEを使用してテーブルを更新する例です。

サブクエリは次のようになります

SELECT
   C1,
   (SELECT C2 FROM SomeTable) as C2
FROM Table

または、相関サブクエリは、a.c1に基づいて結果を参照/結合/制限する場合にOPで提供したものです。

したがって、これらは間違いなく同じものではありませんが、多くの場合、これらのメソッドの1つ以上を使用して同じ結果を得ることができます。それは単にその最終結果が何であるかに依存します。


1

withOracleの句とサブクエリの主な違いは、句内のクエリを複数回参照できることです。その後、materializeヒントを使用して一時テーブルに変換するなど、いくつかの最適化を行うことができます。with句内で自分自身を参照することで、再帰クエリを実行することもできます。インラインビューではこれを行うことはできません。

詳細については、こちらこちらをご覧ください


通常、具体化のヒントは必要ありません。デフォルトでは、OracleオプティマイザーはCTEを具体化することが理にかなっているかどうかを決定しますが、オプティマイザーの評価をヒントMATERIALIZErespで上書きできます。INLINE反対のために。
Wernfried Domscheit

本当です@WernfriedDomscheit。ただし、オプティマイザがCTEの具体化を選択しない場合もあり、その場合は、materializeヒントの使用が有効なオプションです。非常に複雑なクエリを最適化するときに、CTEの具体化が実行計画に利益をもたらすことがわかっていたときに、それを指定する必要がある場合がありました。
Marko Vodopija

0

OracleだけでなくSQLサーバーのCTEにも注意する必要があります。CTEを使用すると、サブクエリやクロス適用などに比べてクエリのパフォーマンスが低下する場合があります。

いつものように、さまざまな負荷条件下でクエリをテストして、どのクエリが最適に機能するかを判断することが重要です。

Oracleでの@scsimonと同様に、MS SQLサーバーは、インデックスの使用に関して期待どおりに動作しない場合があります。

同じデータを2回以上使用する場合は、CTEの方が便利です。データを1回だけ使用する場合は、大規模なデータセットではサブクエリの方が高速です。

たとえば、select * from(my subquery)join other else ...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.