为什么我的光标停在循环中间?

Rya*_*ett 1 t-sql sql-server sql-server-2005

这里发布的代码是'示例'代码,它不是生产代码.我这样做是为了解决我正在解释的可读/简洁的问题.


使用类似于下面的代码,我们遇到了一个奇怪的错误.在每次INSERT之后,WHILE循环停止.

table包含100行,当插入在50行之后完成时,光标停止,只触及前50行.当插入在55之后完成时它在55之后停止,依此类推.

-- This code is an hypothetical example written to express
-- an problem seen in production

DECLARE @v1 int
DECLARE @v2 int

DECLARE MyCursor CURSOR FAST_FORWARD FOR
SELECT Col1, Col2
FROM table

OPEN MyCursor

FETCH NEXT FROM MyCursor INTO @v1, @v2

WHILE(@@FETCH_STATUS=0)
BEGIN

  IF(@v1>10)
  BEGIN
    INSERT INTO table2(col1) VALUES (@v2)
  END

  FETCH NEXT FROM MyCursor INTO @v1, @v2

END

CLOSE MyCursor
DEALLOCATE MyCursor
Run Code Online (Sandbox Code Playgroud)

table2上有一个AFTER INSERT触发器,用于将table2上的变量记录到第三个表中,恰当地命名为mutation.这包含一个插入来处理插入的游标(以非常特定的方式按列记录突变,这需要游标).

一点背景:这存在于一组小支持表上.为了审计目的,项目要求记录对源数据所做的每个更改.带有日志记录的表格包含诸如银行帐号之类的内容,其中将存入大量资金.最多只有几千条记录,它们只应该很少修改.审计功能可以阻止欺诈行为:因为我们记录了"做了什么"的"改变了什么".

实现这一点的明显,快速和合理的方法是每次进行更新时存储整行.然后我们不需要光标,它会更好地执行一个因素.然而,情况的政治意味着我的双手被束缚.

唷.现在回到问题.

简化版本的触发器(真实版本每列插入一次,并且还插入旧值):

--This cursor is an hypothetical cursor written to express
--an problem seen in production.

--On UPDATE a new record must be added to table Mutaties for
--every row in every column in the database.  This is required
--for auditing purposes.

--An set-based approach which stores the previous state of the row
--is expressly forbidden by the customer


DECLARE @col1 int
DECLARE @col2 int
DECLARE @col1_old int
DECLARE @col2_old int

--Loop through old values next to new values
DECLARE MyTriggerCursor CURSOR FAST_FORWARD FOR
SELECT i.col1, i.col2, d.col1 as col1_old, d.col2 as col2_old
FROM Inserted i
  INNER JOIN Deleted d ON i.id=d.id

OPEN MyTriggerCursor 

FETCH NEXT FROM MyTriggerCursor INTO @col1, @col2, @col1_old, @col2_old

--Loop through all rows which were updated
WHILE(@@FETCH_STATUS=0)
BEGIN

    --In production code a few more details are logged, such as userid, times etc etc

    --First column
    INSERT Mutaties (tablename, columnname, newvalue, oldvalue)
    VALUES ('table2', 'col1', @col1, @col1_old)

    --Second column
    INSERT Mutaties (tablename, columnname, newvalue, oldvalue)
    VALUES ('table2', 'col2', @col2, @col1_old)

    FETCH NEXT FROM MyTriggerCursor INTO @col1, @col2, @col1_old, @col2_old

END

CLOSE MyTriggerCursor
DEALLOCATE MyTriggerCursor
Run Code Online (Sandbox Code Playgroud)

为什么代码在循环中间退出?

HLG*_*GEM 9

你的问题是你根本不应该使用光标!这是上面给出的示例的代码.

INSERT INTO table2(col1)
SELECT Col1 FROM table
where col1>10
Run Code Online (Sandbox Code Playgroud)

你也永远不应该在触发器中使用光标,这会破坏性能.如果有人在插入中添加了100,000行,则可能需要几分钟(甚至几小时)而不是毫秒或秒.我们在这里替换了一个(在我接受这项工作之前),并将该表的导入从40分减少到45秒.

应检查使用游标的任何生产代码,以使用正确的基于集合的代码替换它.根据我的经验,90%以上的游标可以以基于集合的方式重新编写.