静的カーソルは読み取り専用であるため、データを変更できません。「現在の場所」で実行すると、期待どおりにエラーが返されます。ここまでは順調ですね。しかし、静的カーソルがこのような変数でデータを変更できることを知って驚いた。
DECLARE @nome varchar(100), @salario int,@idemp int
DECLARE contact_cursor CURSOR STATIC FOR
SELECT empno,ename, sal FROM emp
OPEN contact_cursor;
FETCH NEXT from contact_cursor into @idemp,@nome, @salario
WHILE @@FETCH_STATUS=0
BEGIN
If @salario < 5000
Update Emp
Set Sal = Sal * 1.1
where empno=@idemp --No error and do the update
--Where current of contact_cursor; --gives error
print @nome+' '+cast(@salario as varchar(100));
</ code>
Contact_cursorからNEXTを@ idemp、@ nome、@salarioにフェッチします END CLOSE contact_cursor; DEALLOCATE contact_cursor;
主な違いは、各アプローチが更新する行をどのように見つけるかです。 STATIC
Cursorは最初に完全な結果セットを非表示の一時テーブルにコピーするので(それがなぜ読み取り専用であるのか)、seemとすると、効率が悪くなり、再各UPDATE
のメインテーブルをクエリします。ただし、位置付け更新は、論理読み取りと操作でかなり多くのようです。ただし、位置付け更新の利点の1つは、MSDNページの [〜#〜] update [〜#〜] に示されています。
CURRENT OF
指定したカーソルの現在位置で更新が実行されることを指定します。
WHERE CURRENT OF句を使用した位置付け更新は、カーソルの現在位置にある単一行を更新します。これは、更新する行を修飾するためにWHERE句を使用する検索更新よりも正確です。検索条件が単一の行を一意に識別しない場合、検索された更新は複数の行を変更します。
テスト設定
SET NOCOUNT ON;
-- DROP TABLE ##CursorTest;
CREATE TABLE ##CursorTest ([ID] INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
[Val] INT NOT NULL);
INSERT INTO ##CursorTest ([Val]) VALUES (1), (1), (1), (1);
更新可能なカーソルとWHERE CURRENT OF
UPDATE ##CursorTest SET [Val] = 1;
SELECT * FROM ##CursorTest;
SET STATISTICS IO ON;
DECLARE curTest CURSOR TYPE_WARNING
LOCAL
FORWARD_ONLY
KEYSET -- removing only reduces logical reads by 4
SCROLL_LOCKS
--OPTIMISTIC
FOR
SELECT [ID] FROM ##CursorTest WHERE [Val] < 5
FOR UPDATE OF [Val];
DECLARE @ID INT;
OPEN curTest;
FETCH NEXT
FROM curTest
INTO @ID;
WHILE (@@FETCH_STATUS = 0)
BEGIN
UPDATE tmp
SET tmp.[Val] = tmp.[Val] + 2
FROM ##CursorTest tmp
WHERE CURRENT OF curTest;
FETCH NEXT
FROM curTest
INTO @ID;
END;
CLOSE curTest;
DEALLOCATE curTest;
SET STATISTICS IO OFF;
SELECT * FROM ##CursorTest;
結果:
Table 'Worktable'. Scan count 0, logical reads 8
Table '##CursorTest'. Scan count 1, logical reads 2
Table '##CursorTest'. Scan count 1, logical reads 2
Table 'Worktable'. Scan count 1, logical reads 2
Table '##CursorTest'. Scan count 0, logical reads 2
Table 'Worktable'. Scan count 0, logical reads 2
Table '##CursorTest'. Scan count 1, logical reads 2
Table 'Worktable'. Scan count 1, logical reads 2
Table '##CursorTest'. Scan count 0, logical reads 2
Table 'Worktable'. Scan count 0, logical reads 2
Table '##CursorTest'. Scan count 1, logical reads 2
Table 'Worktable'. Scan count 1, logical reads 2
Table '##CursorTest'. Scan count 0, logical reads 2
Table 'Worktable'. Scan count 0, logical reads 2
Table '##CursorTest'. Scan count 1, logical reads 2
Table 'Worktable'. Scan count 1, logical reads 2
Table '##CursorTest'. Scan count 0, logical reads 2
Table 'Worktable'. Scan count 0, logical reads 2
Table '##CursorTest'. Scan count 0, logical reads 0
Table 'Worktable'. Scan count 1, logical reads 2
KEYSET
オプションを削除すると、論理読み取りが4減少しました(私はそう思います)が、JOINを使用した場合など、より複雑なクエリでは節約にならない可能性があります。
また、切り替えSCROLL_LOCKS
をOPTIMISTIC
にすると、論理読み取りが増加しました。
STATIC
カーソルと標準UPDATE
UPDATE ##CursorTest SET [Val] = 1;
SELECT * FROM ##CursorTest;
SET STATISTICS IO ON;
DECLARE curTest CURSOR TYPE_WARNING
LOCAL
FORWARD_ONLY
STATIC
OPTIMISTIC
FOR
SELECT [ID] FROM ##CursorTest WHERE [Val] < 5;
DECLARE @ID INT;
OPEN curTest;
FETCH NEXT
FROM curTest
INTO @ID;
WHILE (@@FETCH_STATUS = 0)
BEGIN
UPDATE tmp
SET tmp.[Val] = tmp.[Val] + 2
FROM ##CursorTest tmp
WHERE tmp.[ID] = @ID;
FETCH NEXT
FROM curTest
INTO @ID;
END;
CLOSE curTest;
DEALLOCATE curTest;
SET STATISTICS IO OFF;
SELECT * FROM ##CursorTest;
結果:
Table 'Worktable'. Scan count 0, logical reads 8
Table '##CursorTest'. Scan count 1, logical reads 2
Table 'Worktable'. Scan count 0, logical reads 2
Table '##CursorTest'. Scan count 0, logical reads 2
Table 'Worktable'. Scan count 0, logical reads 2
Table '##CursorTest'. Scan count 0, logical reads 2
Table 'Worktable'. Scan count 0, logical reads 2
Table '##CursorTest'. Scan count 0, logical reads 2
Table 'Worktable'. Scan count 0, logical reads 2
Table '##CursorTest'. Scan count 0, logical reads 2
Table 'Worktable'. Scan count 0, logical reads 2
これらの単純なテストでは、STATIC
Cursorと通常のUPDATE
がより良いオプションであるように見えます。また、Cursorのクエリがより複雑な場合は、さらに大きな違いが生じる可能性があります(以下に基づいて更新できる場合)ターゲットテーブルのクラスター化キー)。
ただし、個々の行に絞り込むことができない、または使用するキー値がない場合は、位置付け更新が非常に便利です。
この更新動作をよりよく理解するために、 [〜#〜] update [〜#〜] 構文を確認する必要があるかもしれません。
現在の
指定したカーソルの現在位置で更新が実行されることを指定します。
WHERE CURRENT OF句を使用した位置付け更新は、カーソルの現在位置にある単一行を更新します。これは、更新する行を修飾するためにWHERE句を使用する検索更新よりも正確です。検索条件が単一の行を一意に識別しない場合、検索された更新は複数の行を変更します。
つまり、「CURRENT OF」を使用する場合、これは指定されたカーソルの更新であり、静的カーソル(読み取り専用)を使用すると、投稿したエラーが発生します。
ただし、notを使用して「CURRENT OF」を使用している場合は、基になるテーブルを直接更新しています。この場合、指定されたカーソルとは何の関係もないため、カーソルはReadOnlyまたはNOTです。
[〜#〜] msdn [〜#〜] の静的カーソル定義にも従います
静的
カーソルが使用するデータの一時的なコピーを作成するカーソルを定義します。カーソルへのすべての要求は、tempdbのこの一時テーブルから応答されます。したがって、ベーステーブルに対して行われた変更は、このカーソルに対して行われたフェッチによって返されるデータには反映されず、このカーソルは変更を許可しません。
したがって、このコンテキストでは、この静的カーソルの基になるテーブルを更新しても、変更はカーソル(つまり、カーソルを表す一時テーブル)に反映されません。
簡単なロジックの要約を作成できる場合(自分の理解で)
静的カーソルを作成すると、[TempDB]に一時テーブルが作成され、この一時テーブルは読み取り専用になります。つまり、CURRENT OFを使用してこの一時テーブルを参照したり、一時テーブルを更新したりすることはできません。
ただし、基礎となるテーブルに対して行うことは何でもできます(この静的カーソルの場合)