SQL Server WHILE 循环运行两次有更新,一次没有更新

Ken*_*Ken 3 sql-server

所以这很奇怪。这是我的代码:

PRINT 'Define cursor'
DECLARE cursor1 CURSOR FOR
SELECT  b.EmploymentTypeID
FROM    EmploymentTypes b INNER JOIN #ListEmployments l
        on  b.EmploymentID = l.EmploymentID

PRINT 'Open cursor'
OPEN cursor1
FETCH NEXT FROM cursor1 INTO @pEmploymentTypeID

WHILE @@FETCH_STATUS = 0
BEGIN
 PRINT '1) Inside WHILE loop.  @pEmploymentTypeID: ' + convert(varchar(20), @pEmploymentTypeID)
 PRINT '2) Inside WHILE loop.  @@FETCH_STATUS: ' + convert(varchar(20), @@FETCH_STATUS)
 -- do some work
 UPDATE EmploymentTypes
       SET  EmploymentTypeRD = EmploymentTypeID
    WHERE EmploymentTypeID = @pEmploymentTypeID
      AND EmploymentTypeRD = 0

 PRINT '3) Inside WHILE loop.  Pre-FETCH @@FETCH_STATUS: ' + convert(varchar(20), @@FETCH_STATUS)
 FETCH NEXT FROM cursor1 INTO @pEmploymentTypeID
 PRINT '4) Inside WHILE loop.  Post-FETCH @@FETCH_STATUS: ' + convert(varchar(20), @@FETCH_STATUS)
  
END
CLOSE cursor1
DEALLOCATE cursor1
Run Code Online (Sandbox Code Playgroud)

当我运行它时,循环执行两次,我看到以下输出。

Define cursor
Open cursor
1) Inside WHILE loop.  @pEmploymentTypeID: 695837
2) Inside WHILE loop.  @@FETCH_STATUS: 0
3) Inside WHILE loop.  Pre-FETCH @@FETCH_STATUS: 0
4) Inside WHILE loop.  Post-FETCH @@FETCH_STATUS: 0
1) Inside WHILE loop.  @pEmploymentTypeID: 695837
2) Inside WHILE loop.  @@FETCH_STATUS: 0
3) Inside WHILE loop.  Pre-FETCH @@FETCH_STATUS: 0
4) Inside WHILE loop.  Post-FETCH @@FETCH_STATUS: -1
Ran in ROLLBACK
Run Code Online (Sandbox Code Playgroud)

如果我注释掉“--do some work”注释后面的 UPDATE 语句,则输出为:

Define cursor
Open cursor
1) Inside WHILE loop.  @pEmploymentTypeID: 695837
2) Inside WHILE loop.  @@FETCH_STATUS: 0
3) Inside WHILE loop.  Pre-FETCH @@FETCH_STATUS: 0
4) Inside WHILE loop.  Post-FETCH @@FETCH_STATUS: -1
Ran in ROLLBACK
Run Code Online (Sandbox Code Playgroud)

为什么在启用更新的情况下循环运行两次?

Cha*_*ace 7

您遇到了一个更广为人知的问题“万圣节问题”,换句话说,您正在读取刚刚修改的行。

这是不应使用游标的众多原因之一。

相反,进行简单的联合更新。这是执行脚本中所有操作的单个语句。服务器具有特定的逻辑来考虑读取时修改的行。

UPDATE et
SET EmploymentTypeRD = EmploymentTypeID
FROM EmploymentTypes et
INNER JOIN #ListEmployments l ON et.EmploymentID = l.EmploymentID
WHERE et.EmploymentTypeRD = 0;
Run Code Online (Sandbox Code Playgroud)