質問

ストアド プロシージャでのカーソルの使用は可能な限り避けるべきである (セット ベースのロジックなどに置き換える) ことが一般に受け入れられています。一部のデータを反復する必要があり、読み取り専用で実行できる場合、早送り (読み取り専用前方) カーソルは、たとえば while ループよりも多かれ少なかれ非効率的でしょうか?私の調査によると、カーソル オプションの方が一般的に高速で、読み取りと CPU 時間の使用が少ないようです。私は大規模なテストを行っていませんが、これは他の人が発見したものですか?このタイプのカーソル (早送り) には追加のオーバーヘッドやリソースがかかりますが、それらは私にはわかりませんが、コストがかかる可能性があります。

カーソルを使用しないという話は、実際には、セットベースのアプローチが利用可能な場合にカーソルの使用を回避し、更新可能なカーソルなどを使用することについて話しているのでしょうか。

ありがとう

役に立ちましたか?

解決

SQL Server でカーソルを回避する「ベスト プラクティス」は、SQL Server 2000 以前のバージョンにまで遡ります。SQL 2005 でのエンジンの書き直しにより、特に早送りオプションの導入により、カーソルの問題に関連するほとんどの問題が解決されました。カーソルは必ずしもセットベースより劣っているわけではなく、Oracle PL/SQL (LOOP) で広く使用され、成功しています。

あなたが言う「一般的に受け入れられている」 だった 有効ですが、現在は時代遅れで間違っています。早送りカーソルが宣伝どおりに動作し、実行されるという前提で進めてください。SQL2005 以降の結果に基づいて、いくつかのテストと調査を実行します。

他のヒント

SQL Server 2005 では、早送りカーソルが最適化されていますが、 ない パフォーマンスの点では、セットベースのクエリに近いのは事実です。カーソル ロジックをセットベースのクエリで置き換えることができない状況はほとんどありません。ローカル変数を埋めるために実行を中断し続ける必要があるため、カーソルは常に本質的に遅くなります。

以下に参考文献をいくつか挙げます。この問題を調査する場合、これらは氷山の一角にすぎません。

http://www.code-magazine.com/Article.aspx?quickid=060113

http://dataeducation.com/re-inventing-the-recursive-cte/

この回答は、これまでに提供された回答を統合することを目的としています。

1) 可能な場合は、クエリにセットベースのロジックを使用します。試しに使ってみてください SELECT, INSERT, UPDATE または DELETE 適切な FROM 句またはネストされたクエリ - ほとんどの場合、これらの方が高速になります。

2) 上記が不可能な場合は、SQL Server 2005 以降では FAST FORWARD カーソルは効率的でパフォーマンスが良いため、while ループよりも優先して使用する必要があります。

「FAST FORWARD よりもさらに高速なカーソルが必要な場合は、STATIC カーソルを使用してください。早送りよりも高速です。極端に速くはありませんが、違いはあります。」

そんなに早くない!マイクロソフトによると:「通常、これらの変換が発生すると、カーソル タイプは「より高価な」カーソル タイプに劣化します。一般に、(FAST) FORWARD-ONLY カーソルが最もパフォーマンスが高く、次に DYNAMIC、KEYSET、最後に STATIC が最もパフォーマンスが低くなります。」

から: http://blogs.msdn.com/b/mssqlisv/archive/2006/06/23/644493.aspx

ほとんどの場合、カーソルは回避できますが、必要な場合もあります。

FAST_FORWARD は DYNAMIC であることに注意してください。FORWARD_ONLY は STATIC カーソルで使用できます。

ハロウィンの問題に使ってみて、何が起こるか見てみましょう。

IF OBJECT_ID('Funcionarios') IS NOT NULL
DROP TABLE Funcionarios
GO

CREATE TABLE Funcionarios(ID          Int IDENTITY(1,1) PRIMARY KEY,
                          ContactName Char(7000),
                          Salario     Numeric(18,2));
GO

INSERT INTO Funcionarios(ContactName, Salario) VALUES('Fabiano', 1900)
INSERT INTO Funcionarios(ContactName, Salario) VALUES('Luciano',2050)
INSERT INTO Funcionarios(ContactName, Salario) VALUES('Gilberto', 2070)
INSERT INTO Funcionarios(ContactName, Salario) VALUES('Ivan', 2090)
GO

CREATE NONCLUSTERED INDEX ix_Salario ON Funcionarios(Salario)
GO

-- Halloween problem, will update all rows until then reach 3000 !!!
UPDATE Funcionarios SET Salario = Salario * 1.1
  FROM Funcionarios WITH(index=ix_Salario)
 WHERE Salario < 3000
GO

-- Simulate here with all different CURSOR declarations
-- DYNAMIC update the rows until all of then reach 3000
-- FAST_FORWARD update the rows until all of then reach 3000
-- STATIC update the rows only one time. 

BEGIN TRAN
DECLARE @ID INT
DECLARE TMP_Cursor CURSOR DYNAMIC 
--DECLARE TMP_Cursor CURSOR FAST_FORWARD
--DECLARE TMP_Cursor CURSOR STATIC READ_ONLY FORWARD_ONLY
    FOR SELECT ID 
          FROM Funcionarios WITH(index=ix_Salario)
         WHERE Salario < 3000

OPEN TMP_Cursor

FETCH NEXT FROM TMP_Cursor INTO @ID

WHILE @@FETCH_STATUS = 0
BEGIN
  SELECT * FROM Funcionarios WITH(index=ix_Salario)

  UPDATE Funcionarios SET Salario = Salario * 1.1 
   WHERE ID = @ID

  FETCH NEXT FROM TMP_Cursor INTO @ID
END

CLOSE TMP_Cursor
DEALLOCATE TMP_Cursor

SELECT * FROM Funcionarios

ROLLBACK TRAN
GO

カーソルは一般に単純な while ループよりも記述が難しいため、カーソルを避ける人がいますが、while ループは一時的またはその他のテーブルからデータを常に選択するため、コストがかかる可能性があります。

読み取り専用の早送りであるカーソルを使用すると、データはメモリ内に保持され、ループ用に特別に設計されています。

この記事 平均的なカーソルは while ループよりも 50 倍高速に実行されることが強調されています。

カーソルの使用に代わるいくつかの方法:

ループテンポタブロール由来のテーブル関連のサブQueriesケースステートメント複数の尋問がよくあることがよくありますが、カーソルの操作は非カーーテクニックでも実現できます。

カーソルを使用する必要があることが確実な場合は、処理するレコードの数を可能な限り減らす必要があります。これを行う 1 つの方法は、処理対象のレコードを最初に一時テーブル (元のテーブルではなく、一時テーブル内のレコードを使用するカーソル) に取得することです。このパスが使用される場合、一時テーブルのレコード数が元のテーブルに比べて大幅に減少していると想定されます。レコードが少ないほど、カーソルはより速く完了します。

パフォーマンスに影響を与えるカーソルのプロパティには次のようなものがあります。

前進のみ:FETCH NEXT による最初の行から最後までのカーソルのみの転送をサポートします。KEYSET または STATIC として設定されていない限り、フェッチが呼び出されるたびに SELECT 句が再評価されます。

静的:作成されたデータの一時コピーを作成し、カーソルによって使用されます。これにより、カーソルが呼び出されるたびに再計算されることがなくなり、パフォーマンスが向上します。これにより、カーソル タイプの変更は許可されず、テーブルへの変更はフェッチが呼び出されたときに反映されません。

キーセット:カーソル行は tempdb の下のテーブルに配置され、非キー列への変更はフェッチが呼び出されたときに反映されます。ただし、テーブルに追加された新しいレコードは反映されません。キーセット カーソルを使用すると、SELECT ステートメントは再度評価されません。

動的:テーブルに対するすべての変更はカーソルに反映されます。カーソルは、フェッチが呼び出されるたびに再評価されます。大量のリソースを使用し、パフォーマンスに悪影響を及ぼします。

早送り:カーソルは FORWARD_ONLY などの一方向ですが、カーソルを読み取り専用として指定します。FORWARD_ONLY はパフォーマンスが向上し、カーソルはフェッチごとに再評価されません。プログラミングに適している場合、最高のパフォーマンスが得られます。

楽観的:このオプションは、カーソル内の行を更新するために使用できます。行がフェッチされて更新され、フェッチ操作と更新操作の間に別の行が更新された場合、カーソル更新操作は失敗します。行更新を実行できる OPTIMISTIC カーソルが使用されている場合、別のプロセスによって更新されるべきではありません。

注記:カーソルが指定されていない場合、デフォルトは FORWARD_ONLY です。

マイルの最初の質問に答えるには...

早送り、読み取り専用、静的カーソル (親しみを込めて「消防ホース カーソル」として知られています) は、通常、同等の一時テーブルと While ループと同等かそれよりも高速です。これは、このようなカーソルは単なる一時テーブルと While ループにすぎないためです。舞台裏で少し最適化されています。

さらに付け加えると、エリック Z.Beard はこのスレッドに投稿し、さらに次の質問に答えました...

「セットベースのアプローチが利用可能なときにカーソルの使用を避け、更新可能なカーソルなどを使用することについて、カーソルを使用していないことについてのすべての話です。」

はい。ごく少数の例外を除いて、ほとんどのカーソルと同じことを行う適切なセットベースのコードを作成するのにかかる時間とコードは少なくなり、使用するリソースが大幅に少なくなるという追加の利点があり、通常はカーソルや While ループよりもはるかに高速に実行されます。一般的に言えば、特定の管理タスクを除いて、適切に記述されたセットベースのコードを優先して、これらのタスクは実際には避けるべきです。もちろん、すべての「ルール」には例外がありますが、カーソル、while ループ、およびその他の形式の RBAR の場合、ほとんどの人は指をすべて使わずに片手で例外を数えることができます。;-)

「Hidden RBAR」という概念もあります。これはセットベースのように見えますが、実際にはそうではありません。このタイプの「セットベース」コードは、特定の人々が RBAR メソッドを採用し、「大丈夫」と言う理由です。たとえば、不等号を含む集計 (SUM) 相関サブクエリを使用して累計問題を解決し、累計を計算する方法は、私の本では実際にはセットベースではありません。代わりに、計算される行ごとに、N*(N+1)/2 の割合で他の多くの行に繰り返し「タッチ」する必要があるため、RBAR は強化されています。これは「三角結合」として知られており、完全なデカルト結合 (十字結合または「正方形結合」) の少なくとも半分の悪さがあります。

MS は SQL Server 2005 以降、カーソルの動作方法をいくつか改善してきましたが、「高速カーソル」という用語は、適切に記述されたセットベースのコードと比較すると依然として矛盾しています。それはOracleでも同様です。私は過去に 3 年間という短い間 Oracle で働いていましたが、私の仕事は既存のコードのパフォーマンスを改善することでした。本当に大幅な改善のほとんどは、カーソルをセットベースのコードに変換したときに実現されました。以前は実行に 4 ~ 8 時間かかっていた多くのジョブが、数分、場合によっては数秒に短縮されました。

FAST FORWARD よりもさらに高速なカーソルが必要な場合は、STATIC カーソルを使用してください。早送りよりも高速です。極端に高速というわけではありませんが、違いはあります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top