パフォーマンスが高い、CTEまたは一時テーブルはどれですか。


136

どちらがパフォーマンスが高いですか、CTEそれともTemporary Tables



ユーザーは、technet.microsoft.comの「共通テーブル式使用」ページで背景情報(パフォーマンスに関連しない)を見つけることができます。
シェリダン

回答:


62

コンセプトは違うと思いますが、「チョークとチーズ」と言っても違いはありません。

  • 一時テーブルは、再利用したり、一連のデータに対して複数の処理パスを実行したりするのに適しています。

  • CTEは、再帰的に、または単に読みやすくするために使用できます。
    また、ビューやインラインテーブルの値関数のように、メインクエリで展開されるマクロのように扱うこともできます。

  • 一時テーブルは、スコープに関するいくつかのルールを持つ別のテーブルです

両方(およびテーブル変数も)を使用する場所にプロシージャを格納しました


12
一時テーブルでは、インデックスや統計が必要になることもありますが、CTEでは必要ありません。
CodeCowboyOrg 2014

9
この回答は、CTEがひどいパフォーマンスにつながる可能性があるという事実を十分に強調していないと思います。私は通常、dba.stackexchangeでこの回答を参照します。私が調べてcte vs temporary tablesいる場合、あなたの質問は私の検索エンジンで2番目に表示されるので、この答えはCTEの欠点を強調する必要があります。リンクされた回答のTL; DR:CTEをパフォーマンスに使用しないでください。。CTEのマイナス面を経験したので、その引用に同意します。
TT。

2
@TT。面白い。CTEのパフォーマンスがはるかに優れていることがわかります
Squ1rr3lz 2017

197

場合によります。

まず第一に

共通テーブル式とは何ですか?

(非再帰的)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

計画1

上記の計画では、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または派生テーブルの中間マテリアライズを強制するヒントを提供します。ただし、これに関する統計は作成されず、スプールされた行の数が見積もりと大幅に異なる場合でも、進行中の実行プランが動的に適応して適応することはできません(少なくとも現在のバージョンでは。アダプティブクエリプランは、未来)。


33
これは、実際の質問に答える唯一の回答です(違いは何であるか、どちらが好みであるかではなく、どちらがより良いパフォーマンスを持っているかを尋ねます)。それはその質問に正しく答えます。これは、説明する裏付けとなるデータがある唯一の回答でもあり、他のいくつか(投票数が多い)は、参照や証明がない他の1つよりも優れていると明確に主張しています...明確にするために、これらの回答もすべて間違っています。「それは依存する」
アルカイン55

2
また、よく記述され、よく参照される回答でもあります。真剣に一流。
Dan Williams

50

CTEには用途があります。CTEのデータが小さく、再帰的なテーブルの場合のように読みやすさが大幅に向上している場合です。ただし、そのパフォーマンスは確かにテーブル変数よりも優れておらず、非常に大きなテーブルを処理している場合、一時テーブルはCTEを大幅に上回ります。これは、CTEでインデックスを定義することができず、別のテーブルとの結合が必要な大量のデータがある場合に発生します(CTEは単にマクロのようなものです)。それぞれに数百万行のレコードがある複数のテーブルを結合する場合、CTEは一時テーブルよりもパフォーマンスが大幅に低下します。


9
私自身の経験からこれを見てきました。CTEのパフォーマンスは大幅に低下します。
goku_da_master

7
結果がキャッシュされないため、CTEのパフォーマンスも低下します。したがって、CTEを使用するたびに、クエリ、計画などすべてが再実行されます。
goku_da_master

1
また、dbエンジンは、すべての参照だけでなく、コンシューマークエリのすべてのに対してもクエリを再実行することを選択する場合があります。相関サブクエリとして...必要がない場合は、常にそれを監視する必要があります。
Mike M

一時テーブルはディスクであるSQL Serverのtempdbに格納されますが、インデックスが作成されるという利点があり、SQLオプティマイザーはその場合の選択クエリで適切に機能します。CTEが格納されているdbまたはディスク領域(メモリサイズを超え、IOページングのキューに入れられている場合)はわかりませんが、大量のデータで最適化されることはありません。コンパイラオプション(再コンパイルあり)を使用して高速化したことがあります
rmehra76

33

一時テーブルは常にディスク上にあります。CTEをメモリに保持できる限り、(テーブル変数のように)おそらく高速です。

ただし、繰り返しになりますが、CTE(または一時テーブル変数)のデータ負荷が大きくなりすぎると、それもディスクに格納されるため、大きなメリットはありません。

一般に、一時テーブルよりもCTEの方が好まれます。使用した後でなくなったからです。私はそれを明示的にドロップすることや何かを考える必要はありません。

したがって、最終的には明確な答えはありませんが、個人的には、一時テーブルよりもCTEを使用することをお勧めします。


2
SQLiteとPostgreSQLの場合、一時テーブル自動的に削除されます(通常、セッションの最後に)。他のDBMSについては知りません。
セラーノ

1
CTEは一時的なビューのようなものです。AFAIKデータは保存されないため、メモリに保持されたり、ディスクに保存されたりすることはありません。重要な注意:CTEを使用するたびに、クエリが再度実行されます。
Robは

1
個人的には、CTEが速度の点でTempテーブルより優れているのを見たことがありません。また、一時テーブルを使用すると、デバッグがはるかに簡単になります
Mark Monforti

7

したがって、最適化に割り当てられたクエリは、SQLサーバーで2つのCTEを使用して記述されました。28秒かかりました。

一時テーブルに変換するのに2分かかり、クエリには3秒かかりました

結合されているフィールドの一時テーブルにインデックスを追加して、2秒に下げました

CTEを削除することで、3分の作業と、実行速度がすべて12倍速くなりました。個人的には、CTEもデバッグするのが難しい場合は使用しません。

奇妙なことに、CTEはどちらも一度しか使用されておらず、インデックスを付けても50%高速であることが判明しました。


6

CTEは物理的なスペースを取りません。これは、結合を使用できる単なる結果セットです。

一時テーブルは一時的なものです。すべての変数を定義する必要があるため、インデックスを作成し、通常のテーブルのように制約できます。

セッション内のみの一時テーブルのスコープ。例:2つのSQLクエリウィンドウを開く

create table #temp(empid int,empname varchar)
insert into #temp 
select 101,'xxx'

select * from #temp

最初のウィンドウでこのクエリを実行してから、次のクエリを2番目のウィンドウで実行すると、違いがわかります。

select * from #temp

4
>>「これは、結合を使用できる単なる結果セットです。」->これは正確ではありません。CTEは「結果セット」ではなく、インラインコードです。SQL Serverクエリエンジンは、クエリテキストの一部としてCTEコードを解析し、それに応じて実行プランを構築します。CTEがインラインであるという考えは、CTEを使用することの大きな利点です。これにより、サーバーは「結合実行計画」を作成できるようになります
Ronen Ariely

4

私は両方を使用しましたが、大規模で複雑な手順では、一時テーブルを使用する方がより適切で系統的であることが常にわかります。CTEには用途がありますが、通常はデータが小さいものです。

たとえば、15秒で大きな計算結果が返されるsprocを作成しましたが、このコードをCTEで実行するように変換し、同じ結果を達成するために8分を超えて実行されることを確認しました。


3

パーティーには遅れますが...

私が作業している環境は非常に制約されており、一部のベンダー製品をサポートし、レポートなどの「付加価値」サービスを提供しています。ポリシーと契約の制限により、通常、独立したテーブル/データスペースの豪華さ、および/または永続的なコードを作成する機能(アプリケーションによっては少し改善されます)は許可されません。

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に賛成です(データスペースを取得するまで)。


3

CTEの優れたパフォーマンスが優れていることがわかった1つの用途は、比較的複雑なクエリを、それぞれ数百万行あるいくつかのテーブルに結合する必要がある場合でした。

CTEを使用して、最初にインデックス付きの列に基づいてサブセットを選択し、まずこれらのテーブルをそれぞれ数千の関連する行に切り分けてから、CTEをメインクエリに結合しました。これにより、クエリの実行時間が指数関数的に短縮されました。

CTEの結果はキャッシュされず、テーブル変数がより良い選択だったかもしれませんが、実際に試してみて上記のシナリオに適合することを見つけたかっただけです。


また、私は、私が唯一本当に結果をキャッシュするので、私のクエリに一度CTEを実行参加でCTEを使用するので、私が思うに、この点では、このような大きな問題ではなかった
purchas

1

これは本当に自由回答形式の質問であり、使用方法と一時テーブルのタイプ(テーブル変数または従来のテーブル)にすべて依存します。

従来の一時テーブルは、一時テーブルにデータを格納しますが、一時テーブルは遅くなります。ただし、テーブル変数にはありません。


1

私はこれをテストしたところ、CTEと非CTE(すべてのユニオンインスタンスに対してクエリが入力された)の両方で、約31秒かかりました。CTEはコードをより読みやすくしましたが、241行から130行に削減されました。一方、一時テーブルはそれを132行に削減し、実行に5秒かかりました。冗談抜き。このテストはすべてキャッシュされました。以前はすべてすべてのクエリが実行されていました。


1

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

1
あなたの答えは非常に一般的であるようです...その「CTEが一時テーブルを上回った」ことをどのように測定しますか 時間を測定しましたか?私の意見では、回答を編集して詳細を追加する必要があります。
Il Vic

はい、私の声明を裏付けるための時間測定と実行計画があります。
Amardeep Kohli 2015

権限が制限されているため、実行プランのimgを追加できません。解決したら詳細を更新します
Amardeep Kohli
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.