Reputation: 259
I am having an issue with a CURSOR where in some occasion is populated with only one value but rather than exist straight away it goes on and on till, in my case, crashes.
This is the code:
DECLARE @positionID as INT
DECLARE position_cursor CURSOR FOR
SELECT positionUniqueNumber
FROM position
WHERE position.EmployeeID = @EmployeeID
OPEN position_cursor
FETCH NEXT FROM position_cursor INTO @positionID
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE position
SET positionFromDate = DATEADD(day,@days, positionFromDate)
WHERE position.positionUniqueNumber = @positionID
FETCH NEXT FROM position_cursor INTO @positionID
END
CLOSE position_cursor
DEALLOCATE position_cursor
I would expect the cursor to exist after one run if 'position_cursor' has only one value, instead it goes on and on till it tries to set a Date higher than 9999 and it crashes. A workaround for me would be to check how many record there are in the table that matches the criteria and use the cursor only if there is more than 1 but I would like to find a better way. Thoughts? Thanks
Upvotes: 0
Views: 2214
Reputation: 88996
Here's a repro with a fix commented out. Most people expect STATIC semantics for a cursor, but it's not the default. If your update causes the row to move in the index being read by the cursor it can read the same row twice.
Just always declare your cursors as LOCAL STATIC to avoid these problems.
drop table if exists position
go
create table position(positionuniquenumber int primary key nonclustered, EmployeeId int, positionFromDate DateTime)
create unique clustered index cix_position on position(employeeid,positionFromDate,positionuniquenumber)
insert into position(positionuniquenumber,EmployeeId,positionFromDate) values (1,1,getdate())
go
DECLARE @EmployeeID int = 1
declare @Days int = 1
DECLARE @positionID as INT
DECLARE position_cursor CURSOR /*LOCAL STATIC*/ FOR
SELECT positionUniqueNumber
FROM position
WHERE position.EmployeeID = @EmployeeID
OPEN position_cursor
FETCH NEXT FROM position_cursor INTO @positionID
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE position
SET positionFromDate = DATEADD(day,@days, positionFromDate)
WHERE position.positionUniqueNumber = @positionID
FETCH NEXT FROM position_cursor INTO @positionID
END
CLOSE position_cursor
DEALLOCATE position_cursor
Upvotes: 2