使用触发器更新值

时间:2011-05-16 14:17:20

标签: stored-procedures triggers sql-server-2000 sql-job

我正在尝试增强SQL Server 2000作业的性能。这是场景:
A有最大值300,000行。如果我更新/删除第100行(基于插入时间),在该行之后添加的所有行都应更新其值。排号101,应根据行号更新其值。 100和行号。 102应该根据行号101的更新值更新其值。例如

旧表:

ID...........Value  
100..........220  
101..........(220/2) = 110  
102..........(110/2)=55  
......................

第100行更新了新值:300。

新表

ID...........Value  
100..........300  
101..........(300/2) = 150  
102..........(150/2)=75  
......................  

实际值计算更复杂。这个公式是为了简单起见。

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

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

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

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

谢谢。

2 个答案:

答案 0 :(得分:1)

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

在一些奇迹般的情况下,你可以期待的最好的是将这10天变成10分钟,但从来没有10秒。我建议彻底解释为什么这似乎是必要的,以便可以探索另一种方法。

答案 1 :(得分:1)

首先,关于更新过程。我明白,在更新下一行时,你的程序只是自称。有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);

语句的OPTION (MAXDOP 1)位将语句的并行度限制为1,从而确保行按顺序更新,并且每个值都基于前一个值,即来自行的值前面的ID值。此外,ID列应该成为聚簇索引,当它成为主键时,它通常是默认的。

更新过程的其他功能,即删除和重新创建触发器,可能应该被禁用并重新启用替换:

ALTER TABLE yourtable DISABLE TRIGGER yourtabletrigger

/* the update part */

ALTER TABLE yourtable ENABLE TRIGGER yourtabletrigger

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

那么,我们没有触及触发器。

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

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

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语句这次略有不同。我明白,你的触发器是FOR UPDATE(= AFTER UPDATE),这意味着@id行已经由用户更新了。因此,该过程应该跳过它并从下一行开始,更新表达式现在可以只是公式。

总之,我想说我的测试更新涉及我的表300,000行中的299,995,并且在我不那么快的系统上花了大约3秒钟。当然,没有记录,但我认为应该给出你的速度基本情况。