静态光标和当前位置

and*_*rea 4 sql-server t-sql cursors update

静态游标不允许修改数据,因为它是只读的,并且当使用“Where current of”执行时,它会按预期返回错误。到目前为止,一切都很好。但我惊讶地发现静态游标允许使用这样的变量修改数据。

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));
Run Code Online (Sandbox Code Playgroud)

FETCH NEXT from contact_cursor into @idemp,@nome, @salario END CLOSE contact_cursor; DEALLOCATE contact_cursor;

Run Code Online (Sandbox Code Playgroud) 问题是:在这次更新中使用“where current”和用光标提取的变量有什么区别?

Sol*_*zky 5

主要区别似乎在于每种方法如何找到要更新的行。游标STATIC首先将完整结果集复制到隐藏的临时表(因此它是只读的),因此必须为每个 重新查询主表似乎效率较低UPDATE。然而,定位更新似乎在逻辑读取和操作方面有更多的内容。不过,MSDN 页面的UPDATE中指出了定位更新的一个优点:

当前的

指定在指定游标的当前位置执行更新。

使用 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);
Run Code Online (Sandbox Code Playgroud)

可更新的游标和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;
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

删除该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;
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

这些简单的测试似乎表明STATIC游标和常规UPDATE是更好的选择,并且对游标的更复杂的查询可能会产生更大的差异(假设您能够根据目标表的聚集键进行更新)。

但是,如果您遇到无法缩小到单个行/没有可用的键值的情况,那么定位更新将非常方便。