いつSQLサーバーでテーブル変数と一時テーブルを使用する必要がありますか?


298

テーブル変数の詳細を学習しています。一時テーブルは常にディスク上にあり、テーブル変数はメモリ内にあると言います。つまり、テーブル変数は一時テーブルよりも少ないIO操作を使用するため、テーブル変数のパフォーマンスは一時テーブルよりも優れています。

ただし、場合によっては、メモリに含めることができないテーブル変数のレコードが多すぎると、テーブル変数が一時テーブルと同様にディスクに配置されます。

しかし、「多すぎるレコード」が何であるかはわかりません。100,000レコード?または1000,000レコード?使用しているテーブル変数がメモリ内にあるかディスク上にあるかをどのようにして知ることができますか?SQL Server 2005には、テーブル変数のスケールを測定したり、テーブル変数がメモリからディスクに配置されたときに通知する関数やツールはありますか?


5
テーブル変数はほとんど常に存在しますtempDB-「メモリ内」は神話です。また、テーブル変数は常にクエリオプティマイザーによって1行だけを保持していると見なされます。さらに多くある場合は、実行計画が深刻に悪化する可能性があります。
marc_s 2012


2
@marc_s-そのステートメントの「ほぼ」を削除できます。それは常に中にありますtempdb(しかし、完全にメモリにある場合もあります)
Martin Smith

2
SQL 2014を使用して、メモリ内にテーブル変数を作成できるようになりました
paparazzo

回答:


362

あなたの質問は、テーブル変数と一時テーブルに関する一般的な誤解のいくつかに屈していることを示しています。

2つのオブジェクトタイプの違いを確認するために、DBAサイトに非常に広範な回答を書きました。これは、ディスクとメモリについてのあなたの質問にも対応します(2つの間の動作に大きな違いは見られませんでした)。

タイトルの質問についてですが、テーブル変数とローカル一時テーブルをいつ使用するかについては、常に選択肢があるとは限りません。たとえば関数では、テーブル変数のみを使用でき、子スコープ内のテーブルに書き込む必要がある場合は、テーブルのみ#tempが実行できます(テーブル値パラメーターは読み取り専用アクセスを許可します)。

選択肢がある場合、いくつかの提案を以下に示します(最も信頼できる方法は、特定のワークロードで両方を単純にテストすることです)。

  1. テーブル変数に作成できないインデックスが必要な場合は、もちろん#temporaryテーブルが必要になります。ただし、この詳細はバージョンによって異なります。SQL Server 2012以前では、テーブル変数に作成できる唯一のインデックスは、UNIQUEまたはPRIMARY KEY制約によって暗黙的に作成されたものでした。SQL Server 2014では、で使用可能なオプションのサブセットにインラインインデックス構文が導入されましたCREATE INDEX。これは、フィルターされたインデックス条件を許可するように拡張されました。INCLUDEただし、-d列または列ストアインデックスを含むインデックスは、テーブル変数に作成できません。

  2. テーブルに多数の行を繰り返し追加および削除する場合は、テーブルを使用し#temporaryます。これはTRUNCATEDELETE大きなテーブルよりも効率的です)をサポートし、さらに、aに続く後続の挿入TRUNCATEDELETE 、ここに示すようにaに続くものよりも優れたパフォーマンスを発揮します

  3. 多数の行を削除または更新する場合、一時テーブルは、行変数の共有を使用できる場合(例については、以下の「行セットの共有の影響」を参照)、テーブル変数よりもパフォーマンスが優れている可能性があります。
  4. テーブルを使用した最適な計画がデータによって異なる場合は、テーブルを使用して#temporaryください。これは、データに基づいてプランを動的に再コンパイルできる統計の作成をサポートします(ただし、ストアドプロシージャのキャッシュされた一時テーブルの場合、再コンパイルの動作を個別に理解する必要があります)。
  5. テーブルを使用するクエリの最適なプランが変更される可能性が低い場合は、統計の作成と再コンパイルのオーバーヘッドをスキップするテーブル変数を検討できます(必要なプランを修正するためのヒントが必要になる場合があります)。
  6. テーブルに挿入されたデータのソースが潜在的にコストのかかるSELECTステートメントからのものである場合、テーブル変数を使用すると、並列プランを使用してこれが発生する可能性がブロックされることを考慮してください。
  7. テーブル内のデータが外部ユーザートランザクションのロールバックに耐える必要がある場合は、テーブル変数を使用します。これの考えられる使用例は、長いSQLバッチのさまざまなステップの進行状況をログに記録することです。
  8. #tempユーザートランザクション内でテーブルを使用する場合、ロックはテーブル変数よりも長く保持でき(トランザクションの終わりまで、またはロックの種類と分離レベルに応じてステートメントの終わりまで)、tempdbトランザクションログが切り捨てられるのを防ぐことができます。ユーザートランザクションは終了します。したがって、これはテーブル変数の使用を支持するかもしれません。
  9. ストアドルーチン内では、テーブル変数と一時テーブルの両方をキャッシュできます。キャッシュされたテーブル変数のメタデータメンテナンスは、#temporaryテーブルのそれよりも少なくなります。Bob Wardは彼のtempdbプレゼンテーションで、これにより、同時実行性が高い状況ではシステムテーブルで追加の競合が発生する可能性があると指摘しています。さらに、少量のデータを処理する場合、これはパフォーマンスに測定可能な違いをもたらす可能性があります

行セット共有の影響

DECLARE @T TABLE(id INT PRIMARY KEY, Flag BIT);

CREATE TABLE #T (id INT PRIMARY KEY, Flag BIT);

INSERT INTO @T 
output inserted.* into #T
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID), 0
FROM master..spt_values v1, master..spt_values v2

SET STATISTICS TIME ON

/*CPU time = 7016 ms,  elapsed time = 7860 ms.*/
UPDATE @T SET Flag=1;

/*CPU time = 6234 ms,  elapsed time = 7236 ms.*/
DELETE FROM @T

/* CPU time = 828 ms,  elapsed time = 1120 ms.*/
UPDATE #T SET Flag=1;

/*CPU time = 672 ms,  elapsed time = 980 ms.*/
DELETE FROM #T

DROP TABLE #T

2
こんにちは、ミスター・マーティン・スミス。場合によっては、Storeプロシージャ内の他のクエリで使用するために、一連のID値を保存したいだけです。それで、あなたは私に何を勧めますか?
Jeancarlo Fontalvo 2016年

@JeancarloFontalvo-主キーがオンidで使用されているテーブル変数OPTION (RECOMPILE)はおそらくそれで問題ありませんが、両方をテストします。
マーティン・スミス

メタデータの競合は一時テーブルとテーブル変数の両方で同じですか?
Syed Aqeel Ashiq 2018

@Syed。一般的にテレビでは少ないです。ロックは、ユーザートランザクション内であれば、早期に解放できます。Bob Wardのリンクも参照してください。
マーティン・スミス

73

非常に少量のデータ(数千バイト)の場合は、テーブル変数を使用します

大量のデータに一時テーブルを使用する

それについて考える別の方法:インデックス、自動統計、またはSQLオプティマイザの優れた点の恩恵を受けると思われる場合、データセットはおそらくテーブル変数には大きすぎます。

私の例では、永続テーブルをUPDATE / INSERTするために使用する前に、フォーマットに約20行を入れ、それらをグループとして変更したかっただけです。したがって、テーブル変数は完璧です。

ただし、SQLを実行して一度に数千行をバックフィルしているので、一時テーブルはテーブル変数よりもパフォーマンス優れていると言えます。

これは、CTEが同様のサイズの理由で懸念される方法と同じです。CTEのデータが非常に小さい場合、CTEのパフォーマンスはオプティマイザの結果と同じかそれ以上ですが、非常に大きい場合それはあなたを傷つけます。

私の理解は、主にhttp://www.developerfusion.com/article/84397/table-variables-v-temporary-tables-in-sql-server/に基づいています


テイクアウトは、テーブル変数は小さなデータセットには問題ありませんが、大きなデータセットには一時テーブルを使用します。数千行のクエリがあります。テーブル変数から一時テーブルに切り替えることで、クエリ時間が40秒から5秒に短縮され、他のすべての条件は同じになります。
liang

42

マイクロソフトはここに言います

テーブル変数には分布統計がないため、再コンパイルはトリガーされません。したがって、多くの場合、オプティマイザはテーブル変数に行がないことを前提にクエリプランを作成します。このため、行数が多い(100を超える)と予想される場合は、テーブル変数の使用に注意する必要があります。この場合、一時テーブルがより良い解決策になる場合があります。


14

私はそろばんに完全に同意します(申し訳ありません-コメントするのに十分なポイントがありません)。

また、それは必ずしもまで来ていない覚えておいてどのように多くのあなたが持つレコードが、サイズあなたの記録の。

たとえば、各50列の1,000レコードと各5列のみの100,000レコードのパフォーマンスの違いを考慮しましたか?

最後に、多分あなたは必要以上のデータをクエリ/保存していますか?SQL最適化戦略については、こちらをご覧ください。特にすべてを使用していない場合は、プルするデータの量を制限します(一部のSQLプログラマーは遅延し、小さなサブセットしか使用しない場合でもすべてを選択するだけです)。SQLクエリアナライザーもあなたの親友になる可能性があることを忘れないでください。


4

変数テーブルは現在のセッションでのみ使用できます。たとえば、現在のセッションEXEC内で別のストアドプロシージャが必要な場合は、テーブルを渡す必要があります。Table Valued Parameterもちろん、これはパフォーマンスに影響します。一時テーブルでは、一時テーブル名を渡す

一時テーブルをテストするには:

  • Open Management Studioクエリエディター
  • 一時テーブルを作成する
  • 別のクエリエディターウィンドウを開く
  • このテーブルから選択 "利用可能"

変数テーブルをテストするには:

  • Open Management Studioクエリエディター
  • 変数テーブルを作成する
  • 別のクエリエディターウィンドウを開く
  • このテーブルから選択してください "使用不可"

私が経験した他のことは、次のとおりです。スキーマにGRANTテーブルを作成する権限がない場合は、変数テーブルを使用します。


3

宣言されたテーブルにデータを書き込みdeclare @tb、他のテーブルと結合した後、一時テーブルと比較して応答時間tempdb .. # tbがはるかに長いことに気付きました。

私はそれらを結合する場合@tb時間が異なり、その結果を返すためにはるかに長いです#tm、リターンはほとんど瞬間的です。

私は10,000行の結合でテストを行い、他の5つのテーブルと結合しました


これらの数値を得るために実行したテストを投稿できますか?
Dan Def
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.