web-dev-qa-db-ja.com

私はいつSQL Serverで一時変数と対になるテーブル変数を使用すべきですか?

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

ただし、メモリに格納できないテーブル変数内のレコードが多すぎると、一時テーブルのようにテーブル変数がディスクに書き込まれることがあります。

しかし、「多すぎるレコード」が何であるかはわかりません。 10万レコード?または100万レコード?使用しているテーブル変数がメモリ内にあるのか、ディスク上にあるのかを確認する方法はありますか。 SQL Server 2005には、テーブル変数のスケールを測定するための関数やツールはありますか。それとも、テーブル変数がメモリからディスクに書き込まれるタイミングを教えてもらえますか。

269
yman

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

私は DBAサイトで大々的な回答を書いています 2つのオブジェクトタイプの違いを調べました。これは、ディスクとメモリに関する質問にも対処します(この2つの動作に大きな違いは見られませんでした)。

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

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

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

  2. テーブルに多数の行を繰り返し追加および削除する場合は、#temporaryテーブルを使用します。それはTRUNCATE(大きなテーブルのDELETEよりも効率的)をサポートし、さらにTRUNCATEに続く挿入はDELETEに続く挿入よりもパフォーマンスが向上する可能性があります ここに示すように

  3. 多数の行を削除または更新する場合、一時テーブルはテーブル変数よりもはるかに優れたパフォーマンスを発揮します-行セット共有を使用できる場合(例については、「行セット共有の効果」を参照してください)。
  4. テーブルを使用する最適なプランがデータによって異なる場合は、#temporaryテーブルを使用します。統計の作成をサポートし、データに応じてプランを動的に再コンパイルできるようにします(ただし、ストアドプロシージャのキャッシュされた一時テーブルの場合は、 再コンパイルの動作 を個別に理解する必要があります)。
  5. テーブルを使用したクエリの最適なプランが変更される可能性が低い場合、統計の作成と再コンパイルのオーバーヘッドをスキップするテーブル変数を検討できます(必要なプランを修正するためのヒントが必要になる場合があります)。
  6. テーブルに挿入されるデータのソースが潜在的に高価なSELECTステートメントからのものである場合、テーブル変数を使用すると、並列プランを使用してこの可能性がブロックされることを考慮してください。
  7. テーブル内のデータが外部ユーザートランザクションのロールバックに耐える必要がある場合は、テーブル変数を使用します。これの可能なユースケースは、長いSQLバッチのさまざまなステップの進行状況を記録することです。
  8. ユーザートランザクション内で#tempテーブルを使用する場合、テーブル変数の場合よりもロックを長く保持することができます(トランザクションの終了対ステートメントの終了までロックの種類と分離レベルに依存する可能性があります)。また、ユーザートランザクションが終了するまでtempdbトランザクションログ。したがって、これはテーブル変数の使用を支持するかもしれません。
  9. ストアドルーチン内では、テーブル変数と一時テーブルの両方をキャッシュできます。キャッシュされたテーブル変数のメタデータのメンテナンスは、#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
343
Martin Smith

ごく少量のデータ(数千バイト)の場合は、テーブル変数を使用してください。

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

もう1つの方法:インデックス、自動統計、またはSQLオプティマイザーの長所の恩恵を受ける可能性があると思われる場合は、データセットがテーブル変数には大きすぎる可能性があります。

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

しかし、私もSQLを実行して一度に何千もの行を埋め尽くしています。一時テーブルはテーブル変数よりもmuchの方がパフォーマンスが良いと言えます。

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

私の理解は主に http://www.developerfusion.com/article/84397/table-variables-v-temporary-tables-in-sql-server/ に基づいています。もっと詳しく。

66
Abacus

Microsoft はここに言います

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

35
Paul Sturm

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

また、レコード数になるとは限りませんが、sizeあなたの記録の。

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

最後に、必要以上に多くのデータをクエリ/保存しているのでしょうか。これは SQL最適化戦略 についての良い記事です。あなたがそれをすべて使用していない場合は特に、あなたが引っ張っているデータの量を制限してください。 SQLクエリアナライザもあなたの親友になるかもしれないことを忘れないでください。

14

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

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

  • オープンマネジメントスタジオクエリエディタ
  • 一時テーブルを作成する
  • 別のクエリエディタウィンドウを開く
  • このテーブルから選択する

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

  • オープンマネジメントスタジオクエリエディタ
  • 変数テーブルを作成する
  • 別のクエリエディタウィンドウを開く
  • このテーブルから選択する "利用不可"

あなたのスキーマがテーブルを作成するGRANT権限を持っていない場合は、変数テーブルを使用してください。

4
Mina Gabriel

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

@ tbで結合すると、#tmとは異なり、結果が返されるまでの時間がはるかに長くなります。 。

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

3
César Augusto