web-dev-qa-db-ja.com

SQL Server:パフォーマンスの挿入先と選択先

同僚や研究からさまざまなことを聞​​いています。一時テーブルを作成するときのパフォーマンスの優れたガイドラインは何ですか?小さなテーブルの場合、違いはごくわずかです。

例:テーブルには20列、5,000万行あります。

私はDBAの状態を示しましたが、コンパイラー/パーサーはその場で列のデータ型を見つける必要がないため、挿入先が高速です。 Select intoを示す他の人はより速いです。パフォーマンステストを実施しましたが、select intoの方が少し速いようです。

どちらがより速く、なぜであるかを理解するための良い原則は何ですか? Microsoftは、注意深くプログラミングするために、への挿入を同じくらい高速に行うように最適化すると思います。

記事は次のように述べています。

SQL ServerのSELECT INTOと一時テーブルのINSERT INTOのパフォーマンス

INSERT ... INTOコマンドは、挿入/更新/削除操作のためにキャッシュに作成されたデータページを再利用します。また、テーブルがドロップされると、テーブルが切り捨てられます。 SELECT ... INTOコマンドは、通常のテーブルと同様のテーブル作成用の新しいページを作成し、一時テーブルが削除されると物理的にそれらを削除します。

問題は、Microsoftがselect intoと同じ速度でinsert intoを実行するように最適化しないのはなぜですか?

データウェアハウス用に作成するストアドプロシージャは500以上あり、一時的な使用には適切なガイドラインが必要です。

この記事では、実際にはパフォーマンスと理由に焦点を当てていません。

INSERT INTO vs SELECT INTO

記事の人は良い点を述べました:

これは主に、SQL Serverが宛先テーブルの競合がないことを認識しているためです。 #temp with(tablock)select * from ..への挿入のパフォーマンスは、select * into #temp fromのパフォーマンスとほぼ同じです。

2
user172193

あなたは2つの異なることを論じている2つの異なる記事を引用しました。

最初の記事は_insert..select_を_select into_と一時テーブルについて比較し、2番目の記事はこれら2つを比較します =)一般的に

一般に、_insert..select_は完全にログに記録される操作であるため、低速です。 _select into_は、simpleおよび_bulk logged_復旧モデルに最小限記録されます。

あなたが引用した最後のコメントはinsert into with(tablock)に関するものです。このwith(tablock)は、いくつかの追加条件の下で_insert into_を最小限に記録できます。これはヒープであり、インデックスを持たないはずです。

完全なガイドはここにあります: データ読み込みパフォーマンスガイド

次の表にまとめます。

enter image description here

SQL Server 2016以降の更新に注意してください SQL Server 2016、最小限のログ記録と一括読み込み操作でのBatchsizeの影響 Parik Savjani(Microsoft SQL Server Tigerチームのシニアプログラムマネージャー)。更新されたテーブルは次のとおりです。

updated chart for SQL Server 2016 onward

最初の記事について。 一時テーブルの特定のケースについて説明します。

tempdbデータベースは、単純な復旧モデルに常に含まれ、tempdbへのログインが異なるため、特別です。サーバーを再起動するたびにtempdbが再作成されます。これは、tempdbのクラッシュリカバリが行われないことを意味します。これは、tempdbへのログインに変更後のイメージが不要であることを意味しますデータ、必要に応じてロールバックを実行できる「前」のイメージのみ。これは、tempdbヒントがなくても_insert into..select_も最小限にtablockにログインするという事実につながります(最初の記事で説明したheapの場合) 。

結論「ロギングアスペクトの下での_select into_対_insert..select_」:

heapの場合、_insert into..select_は一時テーブルの場合の_select into_と同様に機能し、tablockヒントを使用しない場合は一般に遅くなります。

2番目の側面は、並列実行の可能性です。

_Select into_はSQL Server 2014以降で並列実行でき、並列_insert...select_はSQL Server 2016で初めて実装されました。

SQL Server 2012の一時テーブルの_select into_と_insert into ..select_のパフォーマンスの違いは再現されませんでした。これらはすべてシリアルで実行されました。

2
sepupic

どちらがより速く、なぜであるかを理解するための良い原則は何ですか? Microsoftは、注意深くプログラミングするために、への挿入を同じくらい高速に行うように最適化すると思います。

この質問のようなものを分析するときに私が従おうとする原則は次のとおりです。

  1. 不必要な仮定をしないでください。
  2. 公式ドキュメントを読んでください。
  3. ワークロードをテストします。テストの量は、コードをどれだけ速くする必要があるかによって異なります。

私はあなたの質問に対処する2つのドキュメントを知っています。最初のものは ブログ投稿 で、一時テーブルのSELECT INTOは、SQL Server 2014の時点で、熱心な書き込みに対して異なる動作をすることを示しています。これは仕様によるものです。ですから、小さなテーブルの場合、その差は最小限であると言っても間違いだとは思いません。どちらかと言えば、ブログの投稿で説明されている最適化は、小さなテーブル向けに設計されているようです。

SQL Server 2014の変更は、これらのページをTEMPDBデータファイルにすばやくフラッシュする必要性を緩和することです。 …#tmp…への選択を行うか、TEMPDB内のSORTを使用してインデックスを作成すると、SQL Serverはこれが一時的な操作である可能性があることを認識します。このような操作に関連付けられたページは、非常に短い時間枠で作成、ロード、クエリ、および解放できます。

例:8ミリ秒で実行されるストアドプロシージャを使用できます。そのストアドプロシージャで、選択して…#tmp…を選択し、#tmpを使用して、ストアドプロシージャの完了時にドロップします。

SQL Server 2014より前のバージョンでは、select intoによって、蓄積されたすべてのページがディスクに書き込まれた可能性があります。 SQL Server 2014の熱心な書き込み動作により、これらのページが以前のバージョンほど迅速にディスクに強制されなくなりました。この動作により、ページがRAM(バッファプール)に格納され、クエリが実行され、テーブルが削除(バッファプールから削除され、フリーリストに返されます)されます。可能な場合はTEMPDBのパフォーマンスを回避して物理I/Oを回避することで、一括操作が大幅に増加し、I/Oパスリソースへの影響も軽減されます。

ドキュメントの2番目の部分 は、SQL Server 2016でTABLOCKヒントなしでINSERT INTO ... SELECTを使用して一時テーブルに並列挿入できるが、TABLOCKヒントが必要であることを説明していますSP1および将来のバージョンで。

この問題は、SQL Server 2016 Service Pack 1で最初に修正されました。 SQL Server 2016 SP1を適用すると、ローカル一時テーブルへのINSERT..SELECTの並列INSERTがデフォルトで無効になり、PFSページの競合が減少し、同時ワークロードの全体的なパフォーマンスが向上します。ローカル一時テーブルへの並列INSERTが必要な場合、ユーザーはローカル一時テーブルに挿入するときにTABLOCKヒントを使用する必要があります。

元のステートメントに戻ると、2つのうちどちらが高速になるかを論理的に推測することはできません。何が速くなるかは、Microsoftがソフトウェアをどのように設計したか、およびワークロードの特性によって異なります。列の定義を作成するのに必要な時間を推測するだけでは役に立ちません。テストは役に立ちます。テストでSELECT INTOの方がそれよりも速いことがわかった場合。それだけの価値があるので、私はパフォーマンスに注意を払ってデータウェアハウスの読み込みにも取り組んでおり、2つのアプローチの違いが心配に値するものであるのを見たことはありません。

4
Joe Obbish

SQL Serverが扱うすべての値にはデータ型があります。 SELECTの結果はすべて型指定されています。そのため、SELECT..INTOはその場で推定データ型を持っている必要はありません-それらはSELECTによって定義されます。

対照的に、INSERT..SELECTを使用すると、ソース列と宛先列は異なる型になりますが、互換性があります。その後、CPUサイクルを消費する暗黙の型強制が行われます。実行時間の違いを測定できるかどうかはわかりませんでした。

1
Michael Green