Stored procedures 使用触发器更新值

Stored procedures 使用触发器更新值,stored-procedures,triggers,sql-server-2000,sql-job,Stored Procedures,Triggers,Sql Server 2000,Sql Job,我试图提高SQLServer2000作业的性能。以下是场景: 表A最多有300000行。如果我更新/删除第100行(基于插入时间),则在该行之后添加的所有行都应更新其值。第101行应根据第100行更新其值,第102行应根据第101行的更新值更新其值。e、 g 旧表: ID...........Value 100..........220 101..........(220/2) = 110 102..........(110/2)=55 .....................

我试图提高SQLServer2000作业的性能。以下是场景:
A
最多有300000行。如果我更新/删除第100行(基于插入时间),则在该行之后添加的所有行都应更新其值。第101行应根据第100行更新其值,第102行应根据第101行的更新值更新其值。e、 g

旧表:

ID...........Value  
100..........220  
101..........(220/2) = 110  
102..........(110/2)=55  
......................
ID...........Value  
100..........300  
101..........(300/2) = 150  
102..........(150/2)=75  
......................  
第100行更新为新值:300

新表格

ID...........Value  
100..........220  
101..........(220/2) = 110  
102..........(110/2)=55  
......................
ID...........Value  
100..........300  
101..........(300/2) = 150  
102..........(150/2)=75  
......................  
实际值的计算更为复杂。这个公式是为了简单

现在,为update/delete语句定义了一个触发器。当一行被更新或删除时,触发器会将该行的数据添加到日志表中。此外,更新/删除后在代码隐藏中创建一个SQL作业,该作业触发一个存储过程,该存储过程最终遍历表
a
的所有下一行并更新它们的值。对于300000行,该过程需要约10天才能完成

启动SP时,它会更新下一行的值。我认为这会导致触发器在每次SP更新时再次运行,并将这些行添加到日志表中。此外,根据客户的要求,任务应在数据库端完成

解决问题:
修改存储过程并直接从触发器调用它。然后,存储过程删除触发器并更新下一行的值,然后再次创建触发器

  • 将有多个程序实例同时运行。如果另一个用户在执行SP时修改一行,系统将不会触发触发器,我将遇到麻烦!有什么解决办法吗
  • 你对这个解决方案有什么看法?有没有更好的方法来实现这一点

谢谢。

这里有一个很大的理论问题。当更新一行需要更新其他行时,它总是非常可疑。这表明数据模型中存在一个严重缺陷。并不是说它永远都不合适,只是需要它的次数远比人们想象的少。当这样的事情是绝对必要的时候,它们通常作为批处理操作来完成


在某些奇迹般的情况下,你能指望的最好的办法就是把这10天变成10分钟,而不是10秒。我建议彻底解释为什么这似乎是必要的,以便可以探索另一种方法。

首先,关于更新过程。我理解,在更新下一行时,您的过程只是在调用自身。对于300K行,这肯定不会很快,即使没有日志记录(尽管完成这项工作可能需要的时间要少得多)。但我绝对无法理解的是,如何能够以这种方式更新超过32行,而不达到最大嵌套级别。也许我的行动顺序错了

无论如何,我可能会做不同的事情,只需要一个指令:

UPDATE yourtable
SET @value = Value = CASE ID
                       WHEN @id THEN @value
                       ELSE @value / 2 /* basically, your formula */
                     END
WHERE ID >= @id
OPTION (MAXDOP 1);
语句的
选项(MAXDOP 1)
位将语句的并行度限制为1,从而确保按顺序更新行,并且每个值都基于前一个值,即基于具有前一个ID值的行的值。另外,
ID
列应该成为聚集索引,当它成为主键时,通常默认情况下是聚集索引

更新过程的其他功能,即删除和重新创建触发器,可能应通过禁用和重新启用触发器来替代:

但是,您是说实际上不应该删除/禁用触发器,因为多个用户可能同时更新表

好吧,我们不会动扳机的

相反,我建议在表中添加一个特殊的列,用户不应该意识到这个列,或者至少不应该太在意这个列,并且应该确保永远不要碰它。该列只应通过“级联更新”过程进行更新。通过检查该列是否正在更新,您将知道是否应该调用更新过程和日志记录

所以,在你的触发器中可能有这样的东西:

IF NOT UPDATE(SpecialColumn) BEGIN
  /* assuming that without SpecialColumn only one row can be updated */
  SELECT TOP 1 @id = ID, @value = Value FROM inserted;
  EXEC UpdateProc @id, @value;
  EXEC LogProc ...;
END
UpdateProc
中:

UPDATE yourtable
SET @value = Value = @value / 2,
    SpecialColumn = SpecialColumn /* basically, just anything, since it can
                                     only be updated by this procedure */
WHERE ID > @id
OPTION (MAXDOP 1);
您可能已经注意到UPDATE语句这次略有不同。我理解,您的触发器用于UPDATE(=更新后),这意味着@id行已经将由用户更新。因此,过程应该跳过它,从下一行开始,更新表达式现在可以是公式


最后,我想说,我的测试更新涉及表中300000行中的29995行,在速度不太快的系统上大约需要3秒钟。当然,没有日志记录,但我认为这应该可以让您了解它的速度有多快。

我不确定“第101行应根据第100行更新其值,第102行应根据第101行的更新值更新其值”实际上是什么意思。你能添加一个更新类型的实际例子吗?谢谢!事实上,我也希望有10分钟。客户会完全满意的。这是一个用.NET2编写的会计软件。更新凭证时,需要更新Cardex价格。正如我所提到的,应该一行一行地进行更新。@Kamyar,嗯,我的意思是,如果在凭证更新时所有的Cardex价格似乎都需要更新,那就是设计缺陷。我敢打赌,在满足客户需求的同时,需求可以被设计出来。但是,我们将把它留到另一天。很高兴你找到了答案。@Andrey M:非常非常有用。非常感谢。过几天我会试试你的建议(因为它比我说的要复杂一点,而且在生产环境中,需要非常仔细地完成),看看会发生什么。再次谢谢