回答:
コンセプトは違うと思いますが、「チョークとチーズ」と言っても違いはありません。
一時テーブルは、再利用したり、一連のデータに対して複数の処理パスを実行したりするのに適しています。
CTEは、再帰的に、または単に読みやすくするために使用できます。
また、ビューやインラインテーブルの値関数のように、メインクエリで展開されるマクロのように扱うこともできます。
一時テーブルは、スコープに関するいくつかのルールを持つ別のテーブルです
両方(およびテーブル変数も)を使用する場所にプロシージャを格納しました
場合によります。
まず第一に
共通テーブル式とは何ですか?
(非再帰的)CTEは、SQL Serverでインラインテーブル式としても使用できる他の構成要素と非常によく似ています。派生テーブル、ビュー、およびインラインテーブル値関数。BOLがCTEを「一時的な結果セットと見なすことができる」と述べている一方で、これは純粋に論理的な説明であることに注意してください。多くの場合、それ自体は電子化されていません。
一時テーブルとは何ですか?
これは、tempdbのデータページに格納されている行のコレクションです。データページは部分的または全体的にメモリに常駐する場合があります。さらに、一時テーブルにはインデックスが付けられ、列統計が含まれる場合があります。
テストデータ
CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);
INSERT INTO T(B)
SELECT TOP (1000000) 0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
master..spt_values v2;
例1
WITH CTE1 AS
(
SELECT A,
ABS(B) AS Abs_B,
F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780
上記の計画では、CTE1についての言及はありません。ベーステーブルに直接アクセスするだけで、同じように扱われます。
SELECT A,
ABS(B) AS Abs_B,
F
FROM T
WHERE A = 780
ここでCTEを中間テンポラリテーブルに具体化して書き換えると、生産性が大幅に低下します。
CTE定義の具体化
SELECT A,
ABS(B) AS Abs_B,
F
FROM T
約8GBのデータを一時テーブルにコピーする必要がありますが、そこから選択するオーバーヘッドもあります。
例2
WITH CTE2
AS (SELECT *,
ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM T
WHERE B % 100000 = 0)
SELECT *
FROM CTE2 T1
CROSS APPLY (SELECT TOP (1) *
FROM CTE2 T2
WHERE T2.A > T1.A
ORDER BY T2.A) CA
上記の例では、私のマシンで約4分かかります。
1,000,000のランダムに生成された値の15行のみが述語に一致しますが、これらを見つけるために、費用のかかるテーブルスキャンが16回行われます。
これは、中間結果を具体化するための良い候補になります。同等の一時テーブルの書き換えには25秒かかりました。
INSERT INTO #T
SELECT *,
ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM T
WHERE B % 100000 = 0
SELECT *
FROM #T T1
CROSS APPLY (SELECT TOP (1) *
FROM #T T2
WHERE T2.A > T1.A
ORDER BY T2.A) CA
一時的なテーブルへのクエリの一部の中間マテリアライズは、一度しか評価されなくても、マテリアライズ結果の統計を利用して残りのクエリを再コンパイルできる場合に役立つことがあります。このアプローチの例は、SQL Catの記事「いつ複雑なクエリを分解するか」にあります。
状況によっては、SQL Serverはスプールを使用して、CTEなどの中間結果をキャッシュし、そのサブツリーを再評価する必要がなくなります。これは、(移行された)接続項目で説明されています。CTEまたは派生テーブルの中間マテリアライズを強制するヒントを提供します。ただし、これに関する統計は作成されず、スプールされた行の数が見積もりと大幅に異なる場合でも、進行中の実行プランが動的に適応して適応することはできません(少なくとも現在のバージョンでは。アダプティブクエリプランは、未来)。
CTEには用途があります。CTEのデータが小さく、再帰的なテーブルの場合のように読みやすさが大幅に向上している場合です。ただし、そのパフォーマンスは確かにテーブル変数よりも優れておらず、非常に大きなテーブルを処理している場合、一時テーブルはCTEを大幅に上回ります。これは、CTEでインデックスを定義することができず、別のテーブルとの結合が必要な大量のデータがある場合に発生します(CTEは単にマクロのようなものです)。それぞれに数百万行のレコードがある複数のテーブルを結合する場合、CTEは一時テーブルよりもパフォーマンスが大幅に低下します。
一時テーブルは常にディスク上にあります。CTEをメモリに保持できる限り、(テーブル変数のように)おそらく高速です。
ただし、繰り返しになりますが、CTE(または一時テーブル変数)のデータ負荷が大きくなりすぎると、それもディスクに格納されるため、大きなメリットはありません。
一般に、一時テーブルよりもCTEの方が好まれます。使用した後でなくなったからです。私はそれを明示的にドロップすることや何かを考える必要はありません。
したがって、最終的には明確な答えはありませんが、個人的には、一時テーブルよりもCTEを使用することをお勧めします。
したがって、最適化に割り当てられたクエリは、SQLサーバーで2つのCTEを使用して記述されました。28秒かかりました。
一時テーブルに変換するのに2分かかり、クエリには3秒かかりました
結合されているフィールドの一時テーブルにインデックスを追加して、2秒に下げました
CTEを削除することで、3分の作業と、実行速度がすべて12倍速くなりました。個人的には、CTEもデバッグするのが難しい場合は使用しません。
奇妙なことに、CTEはどちらも一度しか使用されておらず、インデックスを付けても50%高速であることが判明しました。
CTEは物理的なスペースを取りません。これは、結合を使用できる単なる結果セットです。
一時テーブルは一時的なものです。すべての変数を定義する必要があるため、インデックスを作成し、通常のテーブルのように制約できます。
セッション内のみの一時テーブルのスコープ。例:2つのSQLクエリウィンドウを開く
create table #temp(empid int,empname varchar)
insert into #temp
select 101,'xxx'
select * from #temp
最初のウィンドウでこのクエリを実行してから、次のクエリを2番目のウィンドウで実行すると、違いがわかります。
select * from #temp
パーティーには遅れますが...
私が作業している環境は非常に制約されており、一部のベンダー製品をサポートし、レポートなどの「付加価値」サービスを提供しています。ポリシーと契約の制限により、通常、独立したテーブル/データスペースの豪華さ、および/または永続的なコードを作成する機能(アプリケーションによっては少し改善されます)は許可されません。
IOW、できません通常、ストアドプロシージャ、UDF、または一時テーブルなどを開発アプリケーションインターフェイス(Crystal Reports-テーブルの追加/リンク、CR内のwhere句の設定など)を介してすべてを実行する必要があります。 )。小さな節約の恩恵の1つは、Crystalでコマンド(およびSQL式)を使用できることです。通常のテーブルの追加/リンク機能では効率的でないいくつかのことは、SQLコマンドを定義することで実行できます。私はそれを通してCTEを使用し、「リモート」で非常に良い結果を得ています。CTEは、コードの開発、DBAに渡してコンパイル、暗号化、転送、インストールを行う必要がなく、複数レベルのテストを必要とすることなく、レポートのメンテナンスを支援します。ローカルインターフェイスを介してCTEを実行できます。
CRを使用したCTEの欠点は、各レポートが個別であることです。各CTEは、レポートごとに維持する必要があります。SPとUDFを実行できる場所では、複数のレポートで使用できる何かを開発できます。SPへのリンクと、通常のテーブルで作業しているかのようにパラメーターを渡すだけで済みます。CRはSQLコマンドへのパラメーターの処理があまり得意ではないため、CR / CTEの側面が欠けている場合があります。これらの場合、私は通常、十分なデータ(すべてのデータではない)を返すようにCTEを定義してから、CRのレコード選択機能を使用して、それをスライスしてダイシングします。
だから...私の投票はCTEに賛成です(データスペースを取得するまで)。
CTEの優れたパフォーマンスが優れていることがわかった1つの用途は、比較的複雑なクエリを、それぞれ数百万行あるいくつかのテーブルに結合する必要がある場合でした。
CTEを使用して、最初にインデックス付きの列に基づいてサブセットを選択し、まずこれらのテーブルをそれぞれ数千の関連する行に切り分けてから、CTEをメインクエリに結合しました。これにより、クエリの実行時間が指数関数的に短縮されました。
CTEの結果はキャッシュされず、テーブル変数がより良い選択だったかもしれませんが、実際に試してみて上記のシナリオに適合することを見つけたかっただけです。
私はこれをテストしたところ、CTEと非CTE(すべてのユニオンインスタンスに対してクエリが入力された)の両方で、約31秒かかりました。CTEはコードをより読みやすくしましたが、241行から130行に削減されました。一方、一時テーブルはそれを132行に削減し、実行に5秒かかりました。冗談抜き。このテストはすべてキャッシュされました。以前はすべてすべてのクエリが実行されていました。
SQL Serverでの経験から、CTEが一時テーブルよりも優れているシナリオの1つを見つけました
ストアドプロシージャで、複雑なクエリのDataSet(〜100000)を一度だけ使用する必要がありました。
一時テーブルは、プロシージャの実行が遅いSQLでオーバーヘッドを引き起こしていました(一時テーブルは、tempdbに存在し、現在のプロシージャの存続期間中存続する実際のマテリアライズドテーブルであるため)
一方、CTEでは、次のクエリが実行されるまでCTEが持続します。したがって、CTEはスコープが限定された便利なインメモリ構造です。CTEはデフォルトでtempdbを使用しません。
これは、CTEがコードの簡素化と一時テーブルのパフォーマンス向上に役立つ1つのシナリオです。私は次のような2つのCTEを使用しました
WITH CTE1(ID, Name, Display)
AS (SELECT ID,Name,Display from Table1 where <Some Condition>),
CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>)
SELECT CTE2.ID,CTE2.<col3>
FROM CTE2
GO