私はテーブル変数でもっと詳細を学んでいます。つまり、一時テーブルは常にディスク上にあり、テーブル変数はメモリ内にあります。つまり、テーブル変数は一時テーブルよりもIO操作の回数が少ないため、テーブル変数のパフォーマンスは一時テーブルより優れています。
ただし、メモリに格納できないテーブル変数内のレコードが多すぎると、一時テーブルのようにテーブル変数がディスクに書き込まれることがあります。
しかし、「多すぎるレコード」が何であるかはわかりません。 10万レコード?または100万レコード?使用しているテーブル変数がメモリ内にあるのか、ディスク上にあるのかを確認する方法はありますか。 SQL Server 2005には、テーブル変数のスケールを測定するための関数やツールはありますか。それとも、テーブル変数がメモリからディスクに書き込まれるタイミングを教えてもらえますか。
あなたの質問は、テーブル変数と一時テーブルを取り巻く一般的な誤解に屈したことを示しています。
私は DBAサイトで大々的な回答を書いています 2つのオブジェクトタイプの違いを調べました。これは、ディスクとメモリに関する質問にも対処します(この2つの動作に大きな違いは見られませんでした)。
タイトルの質問に関しては、テーブル変数を使用する場合とローカル一時テーブルを使用する場合については、常に選択できるとは限りません。たとえば、関数では、テーブル変数のみを使用できます。子スコープのテーブルに書き込む必要がある場合は、#temp
テーブルのみが実行されます(テーブル値パラメーターでは 読み取り専用アクセス )。
選択肢がある場合は、いくつかの提案を以下に示します(最も信頼性の高い方法は、特定のワークロードで両方を単純にテストすることです)。
テーブル変数で作成できないインデックスが必要な場合は、もちろん#temporary
テーブルが必要です。ただし、この詳細はバージョンに依存します。 SQL Server 2012以前では、テーブル変数で作成できるインデックスは、UNIQUE
またはPRIMARY KEY
制約を介して暗黙的に作成されたインデックスのみでした。 SQL Server 2014では、CREATE INDEX
で使用可能なオプションのサブセットにインラインインデックス構文が導入されました。これはフィルターされたインデックス条件を許可するために拡張されました。ただし、INCLUDE
- d列のインデックスまたは列ストアインデックスは、テーブル変数に作成することはできません。
テーブルに多数の行を繰り返し追加および削除する場合は、#temporary
テーブルを使用します。それはTRUNCATE
(大きなテーブルのDELETE
よりも効率的)をサポートし、さらにTRUNCATE
に続く挿入はDELETE
に続く挿入よりもパフォーマンスが向上する可能性があります ここに示すように 。
#temporary
テーブルを使用します。統計の作成をサポートし、データに応じてプランを動的に再コンパイルできるようにします(ただし、ストアドプロシージャのキャッシュされた一時テーブルの場合は、 再コンパイルの動作 を個別に理解する必要があります)。SELECT
ステートメントからのものである場合、テーブル変数を使用すると、並列プランを使用してこの可能性がブロックされることを考慮してください。#temp
テーブルを使用する場合、テーブル変数の場合よりもロックを長く保持することができます(トランザクションの終了対ステートメントの終了までロックの種類と分離レベルに依存する可能性があります)。また、ユーザートランザクションが終了するまでtempdb
トランザクションログ。したがって、これはテーブル変数の使用を支持するかもしれません。#temporary
テーブルのメンテナンスよりも少ないです。 Bob Wardは、 tempdb
presentation で、これにより、同時実行性が高い条件下でシステムテーブルに追加の競合が発生する可能性があると指摘しています。さらに、少量のデータを処理する場合、これにより パフォーマンスとの測定可能な差 になります。行セット共有の効果
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
ごく少量のデータ(数千バイト)の場合は、テーブル変数を使用してください。
大量のデータには一時テーブルを使用
もう1つの方法:インデックス、自動統計、またはSQLオプティマイザーの長所の恩恵を受ける可能性があると思われる場合は、データセットがテーブル変数には大きすぎる可能性があります。
私の例では、永続表のUPDATE/INSERTにそれらを使用する前に、約20行をフォーマットに入れ、それらをグループとして変更したいだけでした。したがって、テーブル変数は完璧です。
しかし、私もSQLを実行して一度に何千もの行を埋め尽くしています。一時テーブルはテーブル変数よりもmuchの方がパフォーマンスが良いと言えます。
これは、CTEが同じサイズの理由で懸念される方法と同じではありません。CTEのデータが非常に小さい場合、CTEのパフォーマンスはオプティマイザーのパフォーマンスと同じかそれ以上であることがわかります。それはあなたを傷つけます。
私の理解は主に http://www.developerfusion.com/article/84397/table-variables-v-temporary-tables-in-sql-server/ に基づいています。もっと詳しく。
Microsoft はここに言います
テーブル変数には分布統計はありません。それらは再コンパイルをトリガーしません。したがって、多くの場合、オプティマイザはテーブル変数に行がないと仮定してクエリプランを作成します。このため、より多くの行数(100を超える)が予想される場合は、テーブル変数の使用には注意が必要です。この場合は、一時テーブルの方が良いでしょう。
私はそろばんに完全に同意します(申し訳ありませんが、コメントするのに十分なポイントがありません)。
また、レコード数になるとは限りませんが、sizeあなたの記録の。
たとえば、各50列の1,000レコードと各5列の100,000レコードのパフォーマンスの違いを検討しましたか。
最後に、必要以上に多くのデータをクエリ/保存しているのでしょうか。これは SQL最適化戦略 についての良い記事です。あなたがそれをすべて使用していない場合は特に、あなたが引っ張っているデータの量を制限してください。 SQLクエリアナライザもあなたの親友になるかもしれないことを忘れないでください。
変数テーブルは現在のセッションでのみ使用可能です。たとえば、現在のセッション内で別のストアドプロシージャをEXEC
する必要がある場合は、テーブルを渡す必要があります。 Table Valued Parameter
そしてもちろんこれはパフォーマンスに影響します。テンポラリテーブルではテンポラリテーブル名を渡すだけでこれができます。
一時テーブルをテストするには
変数テーブルをテストするには
あなたのスキーマがテーブルを作成するGRANT
権限を持っていない場合は、変数テーブルを使用してください。
declare @tb
と宣言されたテーブルにデータを書き込み、他のテーブルと結合した後、一時テーブルtempdb .. # tb
と比較して応答時間がはるかに長いことに気付きました。
@ tbで結合すると、#tmとは異なり、結果が返されるまでの時間がはるかに長くなります。 。
私は10,000行の結合と他の5つのテーブルとの結合でテストを行いました