カーソルの使用と、ほとんどの場合、セットベースのロジックを使用してカーソルを書き直す方が効率的である方法について、何度か書いてきました。
私は現実的です。
カーソルが「必須」である場合があることを知っています。別のストアドプロシージャを呼び出すか、行ごとに電子メールを送信する必要があるか、各データベースに対してメンテナンスタスクを実行するか、または単純に1回限りのタスクを実行します。セットベースに変換するために時間を費やす価値はありません。
今日は(おそらく)どのようにやっていますか
まだカーソルを使用している理由に関係なく、少なくとも、非常に高価なデフォルトオプションを使用しないように注意する必要があります。ほとんどの人は次のようにカーソルを開始します:
DECLARE c CURSOR FOR SELECT whatever FROM ...
繰り返しになりますが、アドホックな1回限りのタスクの場合、これはおそらく問題ありません。しかし、あります…
他の方法
デフォルトを使用していくつかのテストを実行し、それらを LOCAL
などのさまざまなカーソルオプションと比較したかったのです。 、 STATIC
、 READ_ONLY
およびFAST_FORWARD
。 (たくさんのオプションがありますが、人々が使用する最も一般的なタイプのカーソル操作に適用できるため、これらは最も一般的に使用されるものです。)いくつかの異なる組み合わせの生の速度をテストしたかっただけでなく、また、コールドサービスの再起動後とウォームキャッシュの両方でのtempdbとメモリへの影響。
カーソルにフィードすることにしたクエリは、 sys.objects
に対する非常に単純なクエリです。 、AdventureWorks2012サンプルデータベース内。これにより、私のシステム(4GBのRAMを備えた非常に控えめな2コアシステム)で318,500行が返されます:
SELECT c1.[object_id] FROM sys.objects AS c1 CROSS JOIN (SELECT TOP 500 name FROM sys.objects) AS c2;
次に、このクエリをさまざまなオプション(デフォルトを含む)でカーソルにラップし、いくつかのテストを実行して、サーバーの合計メモリ、tempdbに割り当てられたページ( sys.dm_db_task_space_usage
による)を測定しました。 および/またはsys.dm_db_session_space_usage
)、および合計期間。また、GlennBerryとRobertDavisのスクリプトを使用してtempdbの競合を観察しようとしましたが、私のわずかなシステムでは、競合をまったく検出できませんでした。もちろん、私もSSDを使用しており、システム上で他に何も実行されていないため、tempdbがボトルネックになる可能性が高い場合は、これらを独自のテストに追加することをお勧めします。
したがって、最終的にクエリは次のようになり、診断クエリが適切なポイントに追加されました。
DECLARE @i INT = 1; DECLARE c CURSOR -- LOCAL -- LOCAL STATIC -- LOCAL FAST_FORWARD -- LOCAL STATIC READ_ONLY FORWARD_ONLY FOR SELECT c1.[object_id] FROM sys.objects AS c1 CROSS JOIN (SELECT TOP 500 name FROM sys.objects) AS c2 ORDER BY c1.[object_id]; OPEN c; FETCH c INTO @i; WHILE (@@FETCH_STATUS = 0) BEGIN SET @i += 1; -- meaningless operation FETCH c INTO @i; END CLOSE c; DEALLOCATE c;
結果
期間
間違いなく最も重要で一般的な対策は、「どれくらいの時間がかかりましたか?」です。ええと、デフォルトのオプションで(または LOCAL
だけで)カーソルを実行するのにほぼ5倍の時間がかかりました 指定)、 STATIC
のいずれかを指定する場合と比較して またはFAST_FORWARD
:
メモリ
また、各カーソルタイプを実行するときにSQLServerが要求する追加のメモリを測定したいと思いました。そのため、各コールドキャッシュテストの前に再起動して、パフォーマンスカウンターの Total Server Memory(KB)
を測定しました。 各テストの前後。ここでの最良の組み合わせは、 LOCAL FAST_FORWARD
でした。 :
tempdbの使用法
この結果は私にとって驚くべきものでした。静的カーソルの定義は、結果全体をtempdbにコピーすることを意味するため、実際には sys.dm_exec_cursors
で表されます。 SNAPSHOT
として 、カーソルのすべての静的バリアントで、tempdbページのヒットが高くなると予想しました。これはそうではありませんでした。ここでも、デフォルトのカーソルを使用した場合と LOCAL
のみを使用した場合の、tempdbの使用量が約5倍になります。 指定:
結論
何年もの間、カーソルには常に次のオプションを指定する必要があることを強調してきました。
LOCAL STATIC READ_ONLY FORWARD_ONLY
この時点から、さらに順列をテストするか、それが最速のオプションではないケースを見つける機会が得られるまで、次のことをお勧めします。
LOCAL FAST_FORWARD
(余談ですが、 LOCAL
を省略してテストも実行しました オプションであり、違いはごくわずかでした。)
とはいえ、これは*すべての*カーソルに必ずしも当てはまるわけではありません。この場合、私はカーソルからデータを順方向にのみ読み取り、基になるデータを(キーまたは WHERE CURRENT OF <を使用して)更新していないカーソルについてのみ話します。 / code> )。これらは別の日のテストです。