web-dev-qa-db-ja.com

CTEと一時テーブルの違いは何ですか?

共通テーブル式(CTE)と一時テーブルの違いは何ですか?そして、いつどちらを使用すればよいですか?

[〜#〜] cte [〜#〜]

WITH cte (Column1, Column2, Column3)
AS
(
    SELECT Column1, Column2, Column3
    FROM SomeTable
)

SELECT * FROM cte

温度テーブル

SELECT Column1, Column2, Column3
INTO #tmpTable
FROM SomeTable

SELECT * FROM #tmpTable
179
Rachel

これはかなり広いですが、私はできる限り一般的な答えを提供します。

CTEs ...

  • インデックス付けできません(ただし、参照オブジェクトの既存のインデックスを使用できます)
  • 制約を持つことはできません
  • 本質的に使い捨てVIEWs
  • 次のクエリが実行されるまで持続する
  • 再帰的にすることができます
  • 専用の統計情報を持たない(基礎となるオブジェクトの統計情報に依存)

#Tempテーブル...

  • Tempdbに存在する実際のマテリアライズテーブル
  • インデックスを作成できます
  • 制約を持つことができます
  • 現在の接続の存続期間中持続する
  • 他のクエリまたはサブプロシージャで参照できます
  • エンジンによって生成された専用の統計を持っている

それぞれをいつ使用するかについては、非常に異なるユースケースがあります。非常に大きな結果セットがある場合、またはそれを2回以上参照する必要がある場合は、#temp テーブル。再帰的である必要がある場合、使い捨てである場合、または論理的に単純化する場合は、CTEが推奨されます。

また、CTEパフォーマンスのために使用しないでくださいにする必要があります。繰り返しになりますが、CTEを使用することでスピードアップすることはほとんどありません。あなたはそれらを使っていくつかの素晴らしいことをすることができますが、クエリの高速化は実際にはそれらの1つではありません。

204
JNK

編集:

以下のマーティンのコメントをご覧ください:

CTEはメモリ内のテーブルとして具体化されません。これは、クエリ定義をカプセル化する方法にすぎません。 OPの場合はインライン化され、単に_SELECT Column1, Column2, Column3 FROM SomeTable_を実行するのと同じになります。ほとんどの場合、それらは前もって具体化されません。そのため、これは行を返しませんWITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.Xも実行プランをチェックします。ただし、スプールを取得する計画をハッキングすることもできます。このためのヒントを要求する接続項目があります。 –マーティンスミス12年2月15日17:08


元の答え

CTE

MSDNで詳細を読む

CTEはメモリで使用されるテーブルを作成しますが、それに続く特定のクエリに対してのみ有効です。再帰を使用する場合、これは効果的な構造になります。

また、テーブル変数の使用を検討することもできます。これはとして使用されます一時テーブルが使用され、各結合で再マテリアライズする必要なく複数回使用できます。また、今すぐいくつかのレコードを保持する必要がある場合は、次の選択の後にいくつかのレコードを追加し、別の操作の後にいくつかのレコードを追加してから、それらの少数のレコードだけを返すと、これは便利な構造になります。実行後に削除する必要はありません。ほとんどの場合、構文上の砂糖です。ただし、行数を少なく保つと、ディスクに具体化されません。詳細については、 SQL Serverの一時テーブルとテーブル変数の違いは何ですか? を参照してください。

一時テーブル

MSDNで詳細を読む-約40%下にスクロール

一時テーブルは、文字通りディスク上に作成されたテーブルであり、誰でも削除できることがわかっている特定のデータベースにあります。不要になったテーブルを破棄することは、優れた開発者の責任ですが、DBAはそれらをワイプすることもできます。

一時テーブルには、ローカルとグローバルの2種類があります。 MS SQL Serverに関しては、ローカルには_#tableName_の指定を使用し、グローバルには_##tableName_の指定を使用します(単一または二重の#を識別特性として使用することに注意してください)。

一時テーブルでは、テーブル変数やCTEではなく、インデックスなどを適用できます。これらはWordの通常の意味での合法的なテーブルであるためです。


一般に、長いまたは大きいクエリには一時テーブルを使用します。すでに小さなデータセットがあり、小さな何かのために少しのコードをすばやくスクリプト化したい場合は、CTEまたはテーブル変数を使用します。経験と他の人のアドバイスによると、CTEから返される行の数が少ない場合はCTEを使用する必要があります。数値が大きい場合は、一時テーブルにインデックスを付ける機能が役立つでしょう。

29
jcolebrand

受け入れられた答え ここでは「CTEをパフォーマンスに使用してはならない」と述べていますが、それは誤解を招く可能性があります。 CTEと一時テーブルのコンテキストでは、ストアドプロシージャのスイートから一連のジャンクを削除したところです。一時テーブルを使用するオーバーヘッドがほとんどない、またはまったくないと考える人がいたためです。私はロットをCTEに押し込みました、例外プロセス全体で合法的に再利用されるものを除きます。すべてのメトリックで約20%のパフォーマンスが得られました。次に、再帰的な処理を実装しようとしていたすべてのカーソルを削除することにしました。これは私が最大の利益を見たところです。結局、応答時間を10分の1に削減することになりました。

CTEと一時テーブルのユースケースは大きく異なります。万能薬ではありませんが、CTEを理解して正しく使用することで、コードの品質/保守性と速度の両方で本当に優れた改善が見られることを強調しておきます。ハンドルを取得したので、一時テーブルとカーソルをSQL処理の大きな弊害と見なしています。今ではほとんどすべてのテーブル変数とCTEで問題なく対応できます。私のコードはよりクリーンで高速です。

17
Mel Padden

CTEはクエリ内で繰り返し呼び出され、参照されるたびに評価されます-このプロセスは再帰的です。 CTEをパラメーター化することはできますが、それが1回だけ参照される場合は、サブクエリのように動作します。

一時テーブルは物理的に永続化され、インデックスが作成される場合があります。実際には、クエリオプティマイザーは中間の結合やサブクエリの結果をスプール操作などの背後で永続化することもあるため、CTEの結果がディスクに永続化されないことは厳密にはありません。

一方、IIRCテーブル変数は常にメモリ内構造です。

一時テーブルはtempdbの実際のオブジェクトですが、cteは、再帰を1ステップで整理する構文を簡略化するための複雑なクエリの一種のラッパーにすぎません。

11
Oleg Dok

CTEを使用する主な理由は、row_number()などのウィンドウ関数やその他のさまざまな関数にアクセスするためです。

これは、グループごとに最初または最後の行を非常に迅速かつ効率的に取得するなどのことを実行できることを意味します- ほとんどの実際的なケースでは他の手段よりも効率的

_with reallyfastcte as (
select *, 
row_number() over (partition by groupingcolumn order by sortingcolumn) as rownum
from sometable
)
select *
from reallyfastcte
where rownum = 1;
_

相関サブクエリまたはサブクエリを使用して、上記と同様のクエリを実行できますが、CTEはほとんどすべてのシナリオで高速になります。

さらに、CTEはコードの簡素化に役立ちます。これにより、クエリをより理解し、オプティマイザをより選択的にするためのより多くのビジネスロジックを導入できるため、パフォーマンスが向上する可能性があります。

さらに、ビジネスロジックを理解し、クエリのどの部分を最初に実行する必要があるかを知っている場合、CTEはパフォーマンスを向上させることができます。通常、最も選択的なクエリを最初に置き、次の結合でインデックスを使用してoption(force order)クエリヒント

最後に、CTEはデフォルトでtempdbを使用しないので、CTEを使用することでそのボトルネックの競合を減らすことができます。

データを複数回クエリする必要がある場合、またはクエリを測定し、一時テーブルに挿入してそれを発見する場合は、一時テーブルを使用する必要があります。次に、パフォーマンスが向上するインデックスを追加します。

8
Dave Hilditch

CTEについては、ここで少し否定的なことがあるようです。

CTEについての私の理解は、それは基本的に一種のアドホックビューであるということです。 SQLは宣言型言語であり、セットベースの言語でもあります。 CTEはセットを宣言する優れた方法です。 CTEにインデックスを付けることができないのは、実際に行う必要がないためです。これは、クエリを読み書きしやすくするための、一種の構文上の砂糖です。適切なオプティマイザは、基礎となるテーブルのインデックスを使用して最適なアクセスプランを作成します。つまり、基になるテーブルのインデックスアドバイスに従うことで、CTEクエリを効果的に高速化できます。

また、セットをCTEとして定義したからといって、セット内のすべての行を処理する必要があるという意味ではありません。クエリによっては、オプティマイザがクエリを満たすのに「十分な」行を処理する場合があります。多分あなたはあなたのスクリーンのために最初の20かそこらしか必要としませんでした。一時テーブルを作成した場合は、それらすべての行を読み取り/書き込みする必要があります。

これに基づいて、CTEはSQLの優れた機能であり、クエリを読みやすくするあらゆる場所で使用できると言えます。本当にすべてのレコードを処理する必要のあるバッチプロセスの一時テーブルについてのみ考えます。それでも一時的なテーブルでは、データベースがキャッシングとインデックス作成を支援するのがはるかに難しいため、それでもafaikはあまりお勧めできません。トランザクションに固有のPKフィールドを持つ永続テーブルを用意することをお勧めします。

私の経験は主にDB2に関するものであることを認めなければならないので、CTEは両方の製品で同じように機能すると想定しています。 CTEがSQLサーバーで何らかの形で劣っている場合、私は喜んで正直します。 ;)

6
Ben Thurley