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


174

共通テーブル式(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


回答:


200

これはかなり広範ですが、できる限り一般的な回答をします。

CTE ...

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

#温度表...

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

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

また、パフォーマンスのためにa CTE使用しないでください。繰り返しになりますが、これは単なる使い捨てのビューなので、CTEを使用して速度を上げることはほとんどありません。それらを使用してきちんとしたことができますが、クエリの高速化は実際にはそれらの1つではありません。


CTEを使用して大きなMERGEを高速化することは
重要

1
また、CTEを使用して多くのクエリを高速化することも重要です。CTEを使用すると、独自のビジネス知識を追加してクエリオプティマイザを上回ることができるためです。たとえば、結果の行が非常に小さいことがわかっているテーブルからCTEのパート1を選択することができます。同じクエリ内で、この小さな結果セットをいくつかの大きな結果セットに結合し、古い統計などに起因する問題を完全に回避できます。これを行うには、クエリヒントを追加して順序を強制する必要があります。動作し、パフォーマンスが向上します。
デイブヒルディッチ

「パフォーマンスのために使用されることはありません」というのは、広くてやや主観的な表現ですが、あなたの主張は理解しています。ただし、他のコメントに加えて、再帰プロシージャコールやカーソルなどの別の形式の再帰から再帰CTEに切り替えると、CTEを使用することでパフォーマンスが向上する可能性があります。
JD

29

編集:

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

CTEはメモリ内のテーブルとして具体化されません。これは、クエリ定義をカプセル化する方法にすぎません。OPの場合、それはインライン化され、ただ行うのと同じになりますSELECT Column1, Column2, Column3 FROM SomeTable。ほとんどの場合、前もってマテリアライズされません。これがrowsを返さない理由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はメモリで使用されるテーブルを作成しますが、それに続く特定のクエリに対してのみ有効です。再帰を使用する場合、これは効果的な構造になります。

テーブル変数の使用を検討することもできます。これが使用されるように一時テーブルが使用され、それぞれに対して参加再マテリアライズする必要なしに複数回使用することができます。また、ここでいくつかのレコードを永続化する必要がある場合、次の選択後にさらにレコードを追加し、別のopの後にさらにレコードを追加し、それらの少数のレコードのみを返します。これは便利な構造です。実行後にドロップする必要はありません。ほとんどが構文糖です。ただし、行数を低く保つと、ディスクにマテリアライズされません。SQL Serverの一時テーブルとテーブル変数の違いは何ですか?をご覧ください詳細については。

一時テーブル

MSDNで詳細を読む-約40%スクロールダウン

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

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

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


一般的に、より長いまたはより大きなクエリには一時テーブルを使用し、小さなデータセットが既にあり、小さなコードのコードをすぐにスクリプト化する場合は、CTEまたはテーブル変数を使用します。他の人の経験とアドバイスから、CTEから返される行の数が少ない場合はCTEを使用する必要があることが示されています。数が多い場合は、おそらく一時テーブルでインデックスを作成できると便利です。


11
CTEはメモリ内のテーブルとして具体化されません。これは、クエリ定義をカプセル化する方法にすぎません。OPの場合には、インライン化されて、ちょうどやって同じSELECT Column1, Column2, Column3 FROM SomeTable
マーティン・スミス

4
ほとんどの場合、前もってマテリアライズされません。これがrowsを返さない理由WITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.Xです。また、実行計画もチェックします。ただし、スプールを取得する計画ハッキングすることもできます。このためのヒントを要求する接続アイテムがあります。
マーティンスミス

16

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

CTEと一時テーブルの使用例は非常に異なります。万能薬ではありませんが、CTEを理解して正しく使用することで、コードの品質/保守性と速度の両方で本当に素晴らしい改善がもたらされることを強調したいと思います。それらのハンドルを取得したので、一時テーブルとカーソルはSQL処理の大きな弊害であると考えています。現在、ほぼすべてのテーブル変数とCTEを使用して問題なく取得できます。私のコードはよりクリーンで高速です。


さて、公平にしましょう-カーソルは大きな悪です。一時テーブルは、最悪の場合、より小さな悪です。:-) あなたがあなた自身を見たように、それらを同じレベルに置くことは本当に不公平です。
RDFozz

@RDFozzそうです、私たちが知っているように地獄には9つの円があります。一時テーブルを2番目に、カーソルを... 7番目に配置できますか?;)
ypercubeᵀᴹ18年

1
あなたはプログラミングの「大悪」が何であるか知っていますか?特定の技術が悪であると人々が言うとき。カーソルのための場所があります。特定のシナリオで他の手法よりも優れている場合があります。ここには悪はありません-あなたは仕事に適切なツールを使用することを学ぶ必要があります。あなたが何をしているかを測定し、CTE、一時テーブル、またはカーソルが悪であるという誇大宣伝を信じないでください。測定-真実はシナリオに依存するため。
デイブヒルディッチ

@DaveHilditchは公平なコメントですが、非常に多くの状況でカーソルが適切な解決策ではないことを主張することも公正なコメントです。
メルパデン

1
私の経験では、カーソルはそれ自体悪くありません。CURSORSは一般に開発者によって「誤って」使用されます。これは、ほとんどのプログラミング言語では、バッチで考える必要のあるSQLとは対照的に、繰り返し考える必要があるためです。これは職場でよくある間違いであり、開発者はCURSOR以外の問題から抜け出すことができないため、優れたDBAが教えて修正するのに役立ちます。@DaveHilditchは完全に正しい:適切な仕事に適切なツールがあれば十分です。
フィリップ

14

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

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

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


4
CTEはパラメーター化できますか?どうやって?また、テーブル変数は常にメモリ内構造ではありません。関連する質問に対する Martinの優れた回答を参照してください。
ポールホワイト

11

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


8

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を使用することでそのボトルネックの競合を減らすことができます。

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


すべての良い点... +1
メルパデン

6

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

CTEに対する私の理解は、基本的に一種のアドホックビューであるということです。SQLは、宣言型言語とセットベースの言語の両方です。CTEはセットを宣言する素晴らしい方法です!CTEのインデックスを作成できないことは、実際には必要ないため、良いことです。クエリの読み取り/書き込みを簡単にするのは、実際には一種の構文糖衣です。適切なオプティマイザは、基礎となるテーブルのインデックスを使用して最適なアクセスプランを作成します。つまり、基礎となるテーブルのインデックスアドバイスに従うことで、CTEクエリを効率的に高速化できます。

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

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

私の経験は主にDB2であると認めざるを得ないので、CTEは両方の製品で同様の方法で機能すると想定しています。CTEがSQLサーバーで何らかの形で劣っている場合、私は喜んで修正します。;)

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