0

我正在尝试提高 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 更新再次运行,并将这些行也添加到日志表中。此外,该任务应根据客户的要求在数据库端完成。

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

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

谢谢你。

4

2 回答 2

1

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

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

于 2011-05-16T17:08:11.270 回答
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 秒。当然,没有日志记录,但我认为这应该可以让您大致了解它的速度。

于 2011-05-16T20:37:11.850 回答