あなたの質問は、テーブル変数と一時テーブルに関する一般的な誤解のいくつかに屈していることを示しています。
2つのオブジェクトタイプの違いを確認するために、DBAサイトに非常に広範な回答を書きました。これは、ディスクとメモリについてのあなたの質問にも対応します(2つの間の動作に大きな違いは見られませんでした)。
タイトルの質問についてですが、テーブル変数とローカル一時テーブルをいつ使用するかについては、常に選択肢があるとは限りません。たとえば関数では、テーブル変数のみを使用でき、子スコープ内のテーブルに書き込む必要がある場合は、テーブルのみ#temp
が実行できます(テーブル値パラメーターは読み取り専用アクセスを許可します)。
選択肢がある場合、いくつかの提案を以下に示します(最も信頼できる方法は、特定のワークロードで両方を単純にテストすることです)。
テーブル変数に作成できないインデックスが必要な場合は、もちろん#temporary
テーブルが必要になります。ただし、この詳細はバージョンによって異なります。SQL Server 2012以前では、テーブル変数に作成できる唯一のインデックスは、UNIQUE
またはPRIMARY KEY
制約によって暗黙的に作成されたものでした。SQL Server 2014では、で使用可能なオプションのサブセットにインラインインデックス構文が導入されましたCREATE INDEX
。これは、フィルターされたインデックス条件を許可するように拡張されました。INCLUDE
ただし、-d列または列ストアインデックスを含むインデックスは、テーブル変数に作成できません。
テーブルに多数の行を繰り返し追加および削除する場合は、テーブルを使用し#temporary
ます。これはTRUNCATE
(DELETE
大きなテーブルよりも効率的です)をサポートし、さらに、aに続く後続の挿入TRUNCATE
はDELETE
、ここに示すようにaに続くものよりも優れたパフォーマンスを発揮します。
- 多数の行を削除または更新する場合、一時テーブルは、行変数の共有を使用できる場合(例については、以下の「行セットの共有の影響」を参照)、テーブル変数よりもパフォーマンスが優れている可能性があります。
- テーブルを使用した最適な計画がデータによって異なる場合は、テーブルを使用して
#temporary
ください。これは、データに基づいてプランを動的に再コンパイルできる統計の作成をサポートします(ただし、ストアドプロシージャのキャッシュされた一時テーブルの場合、再コンパイルの動作を個別に理解する必要があります)。
- テーブルを使用するクエリの最適なプランが変更される可能性が低い場合は、統計の作成と再コンパイルのオーバーヘッドをスキップするテーブル変数を検討できます(必要なプランを修正するためのヒントが必要になる場合があります)。
- テーブルに挿入されたデータのソースが潜在的にコストのかかる
SELECT
ステートメントからのものである場合、テーブル変数を使用すると、並列プランを使用してこれが発生する可能性がブロックされることを考慮してください。
- テーブル内のデータが外部ユーザートランザクションのロールバックに耐える必要がある場合は、テーブル変数を使用します。これの考えられる使用例は、長いSQLバッチのさまざまなステップの進行状況をログに記録することです。
#temp
ユーザートランザクション内でテーブルを使用する場合、ロックはテーブル変数よりも長く保持でき(トランザクションの終わりまで、またはロックの種類と分離レベルに応じてステートメントの終わりまで)、tempdb
トランザクションログが切り捨てられるのを防ぐことができます。ユーザートランザクションは終了します。したがって、これはテーブル変数の使用を支持するかもしれません。
- ストアドルーチン内では、テーブル変数と一時テーブルの両方をキャッシュできます。キャッシュされたテーブル変数のメタデータメンテナンスは、
#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
tempDB
-「メモリ内」は神話です。また、テーブル変数は常にクエリオプティマイザーによって1行だけを保持していると見なされます。さらに多くある場合は、実行計画が深刻に悪化する可能性があります。