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