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