patternsqlMinor
Static Cursor and Where current of
Viewed 0 times
wherecurrentandstaticcursor
Problem
Static Cursor doesn't allow to modify the data because it's read only, and when executed with "Where current of", it returns error as expected.
So far so good.
But i was surprised to find that static cursor allow to modify data with variable like this.
FETCH NEXT from contact_cursor into @idemp,@nome, @salario
END
CLOSE contact_cursor;
DEALLOCATE contact_cursor;
The question is:
Which is the difference between using "where current" and the variable extracted with the cursor in this update?
So far so good.
But i was surprised to find that static cursor allow to modify data with variable like this.
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));FETCH NEXT from contact_cursor into @idemp,@nome, @salario
END
CLOSE contact_cursor;
DEALLOCATE contact_cursor;
The question is:
Which is the difference between using "where current" and the variable extracted with the cursor in this update?
Solution
The main difference seems to be how each approach finds the row to be updated. The
CURRENT OF
Specifies that the update is performed at the current position of the specified cursor.
A positioned update using a WHERE CURRENT OF clause updates the single row at the current position of the cursor. This can be more accurate than a searched update that uses a WHERE clause to qualify the rows to be updated. A searched update modifies multiple rows when the search condition does not uniquely identify a single row.
Test Setup
Updateable CURSOR and
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
SELECT * FROM ##CursorTest;
SET STATISTICS IO ON;
DECLARE curTest CURSOR TYPE_WARNING
LOCAL
FORWARD_ONLY
STATIC
OPTIMISTIC
FOR
SELECT [ID] FROM ##CursorTest WHERE [Val]
Results:
These simple tests seem to show the
But, if you have a situation where you can't narrow down to a individual row / have no Key value to use, then the Positioned Update would be quite handy.
STATIC Cursor copies the full result set to a hidden temporary table first (hence why it is read-only), so it would seem to be less efficient to then have to re-query the main table for each UPDATE. However, the Positioned Update seems to have quite a bit more in Logical Reads and operations. One advantage of the Positioned Update, however, is noted in the MSDN page for UPDATE:CURRENT OF
Specifies that the update is performed at the current position of the specified cursor.
A positioned update using a WHERE CURRENT OF clause updates the single row at the current position of the cursor. This can be more accurate than a searched update that uses a WHERE clause to qualify the rows to be updated. A searched update modifies multiple rows when the search condition does not uniquely identify a single row.
Test Setup
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);
Updateable CURSOR and
WHERE CURRENT OFUPDATE ##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]
Results:
Table 'Worktable'. Scan count 0, logical reads 8Table '##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
Removing the KEYSET option did reduce the logical reads by 4 (I believe), but that might not be a savings on a more complicated query, possibly with JOINs.
Also, switching SCROLL_LOCKS to be OPTIMISTIC increased the Logical Reads.
STATIC Cursor and standard 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]
Results:
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
These simple tests seem to show the
STATIC Cursor and regular UPDATE being the better option, and a more complicated query for the Cursor might be an even bigger difference (assuming you are able to update based on the Clustered Key of the target table).But, if you have a situation where you can't narrow down to a individual row / have no Key value to use, then the Positioned Update would be quite handy.
Context
StackExchange Database Administrators Q#154640, answer score: 5
Revisions (0)
No revisions yet.