web-dev-qa-db-ja.com

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

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

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

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

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

9
Kshitiz Sharma

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

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

他の回答は構文の違いをかなりカバーしているので、ここでは説明しません。代わりに、この回答は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は、インデックスを使用してそれを判断することもできます。私のマシンでは、クエリは次の計画でほぼ瞬時に終了します。

good plan

繰り返すのが好きではないので、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;

ここに計画があります:

bad plan

それは本当に悪い計画です。インデックスを使用する代わりに、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のクエリが選択されました。

8
Joe Obbish

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
scsimon

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

詳細は here および here を参照してください。

1
Marko Vodopija

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

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

Oracleでの@scsimonと同様に、MS SQLサーバーがインデックスの使用に関して期待したことを実行しない場合があります。

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

例えばselect * from(my subquery)join other else ...

0
Justin