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)
为什么代码在循环中间退出?
你的问题是你根本不应该使用光标!这是上面给出的示例的代码.
INSERT INTO table2(col1)
SELECT Col1 FROM table
where col1>10
Run Code Online (Sandbox Code Playgroud)
你也永远不应该在触发器中使用光标,这会破坏性能.如果有人在插入中添加了100,000行,则可能需要几分钟(甚至几小时)而不是毫秒或秒.我们在这里替换了一个(在我接受这项工作之前),并将该表的导入从40分减少到45秒.
应检查使用游标的任何生产代码,以使用正确的基于集合的代码替换它.根据我的经验,90%以上的游标可以以基于集合的方式重新编写.