web-dev-qa-db-ja.com

静的カーソルと現在の場所

静的カーソルは読み取り専用であるため、データを変更できません。「現在の場所」で実行すると、期待どおりにエラーが返されます。ここまでは順調ですね。しかし、静的カーソルがこのような変数でデータを変更できることを知って驚いた。

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;
4
andrea

主な違いは、各アプローチが更新する行をどのように見つけるかです。 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_LOCKSOPTIMISTICにすると、論理読み取りが増加しました。

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のクエリがより複雑な場合は、さらに大きな違いが生じる可能性があります(以下に基づいて更新できる場合)ターゲットテーブルのクラスター化キー)。

ただし、個々の行に絞り込むことができない、または使用するキー値がない場合は、位置付け更新が非常に便利です。

4
Solomon Rutzky

この更新動作をよりよく理解するために、 [〜#〜] update [〜#〜] 構文を確認する必要があるかもしれません。

現在の

指定したカーソルの現在位置で更新が実行されることを指定します。

WHERE CURRENT OF句を使用した位置付け更新は、カーソルの現在位置にある単一行を更新します。これは、更新する行を修飾するためにWHERE句を使用する検索更新よりも正確です。検索条件が単一の行を一意に識別しない場合、検索された更新は複数の行を変更します。

つまり、「CURRENT OF」を使用する場合、これは指定されたカーソルの更新であり、静的カーソル(読み取り専用)を使用すると、投稿したエラーが発生します。

ただし、notを使用して「CURRENT OF」を使用している場合は、基になるテーブルを直接更新しています。この場合、指定されたカーソルとは何の関係もないため、カーソルはReadOnlyまたはNOTです。

[〜#〜] msdn [〜#〜] の静的カーソル定義にも従います

静的

カーソルが使用するデータの一時的なコピーを作成するカーソルを定義します。カーソルへのすべての要求は、tempdbのこの一時テーブルから応答されます。したがって、ベーステーブルに対して行われた変更は、このカーソルに対して行われたフェッチによって返されるデータには反映されず、このカーソルは変更を許可しません。

したがって、このコンテキストでは、この静的カーソルの基になるテーブルを更新しても、変更はカーソル(つまり、カーソルを表す一時テーブル)に反映されません。

簡単なロジックの要約を作成できる場合(自分の理解で)

静的カーソルを作成すると、[TempDB]に一時テーブルが作成され、この一時テーブルは読み取り専用になります。つまり、CURRENT OFを使用してこの一時テーブルを参照したり、一時テーブルを更新したりすることはできません。

ただし、基礎となるテーブルに対して行うことは何でもできます(この静的カーソルの場合)

3
jyao