触发器甚至在单行更新时更新整个表

时间:2012-03-04 13:52:07

标签: sql-server triggers

以下触发器用于在位置表中更新行时自动执行更新。更改可以一次发生一行,也可以一次发生多行。但是,在更新单行时,启用“locations_geteditdate”时,会在位置表中的所有28K行中写入新的时间戳。我知道我在这里遗漏了一些明显的东西,谢谢你的帮助。

ALTER TRIGGER   [dbo].[locations_geteditdate]
ON  [dbo].[TBL_LOCATIONS]   
instead of update
AS
begin
declare @recs INT
select @recs = COUNT(*)
from dbo.TBL_LOCATIONS a
join inserted i on i.Location_ID = a.Location_ID
if @recs > 0
update dbo.TBL_LOCATIONS
SET EditDate = GETDATE()
end
GO

alter TRIGGER   [dbo].[locations_move_topo]
ON  [dbo].[TBL_LOCATIONS]
for update   
AS   
BEGIN   
  update dbo.TBL_LOCATIONS
  set topo_name = dbo.TLU_TOPO_BOUNDS.name
  FROM  dbo.TBL_LOCATIONS
      inner join dbo.TLU_TOPO_BOUNDS
      on dbo.TBL_LOCATIONS.Location_ID = dbo.TBL_LOCATIONS.Location_ID
      where (TLU_TOPO_BOUNDS.Shape.STContains(TBL_LOCATIONS.SHAPE) = 1) ; 
END

接受的答案:

alter TRIGGER   [dbo].[locations_geteditdate]
ON  [dbo].[TBL_LOCATIONS]   
for update 
as
begin
update dbo.TBL_LOCATIONS
SET EditDate = GETDATE()
from dbo.TBL_LOCATIONS locn
inner join inserted i on i.location_id = locn.Location_ID
end
GO

1 个答案:

答案 0 :(得分:2)

在你的if条件中(在locations_geteditdate中)你没有where子句;因此它包括所有记录:

if @recs > 0
update dbo.TBL_LOCATIONS
SET EditDate = GETDATE()
WHERE ???
end

您正确使用了inserted表来查看已更新的内容,但仅用于识别记录数

因此,阅读您在触发器中添加的代码,看起来您正在尝试执行的操作只是将时间戳应用于表格以显示更新时间。

您至少有以下选项:
1.如果您实际上不需要可识别的日期时间,则可以使用时间戳字段而不是日期时间并自动更新。
2.如果您可以控制对表执行更新的位置,则可以在那里设置EditDate(即在存储过程中)

但是,假设您需要一个可识别的日期时间,则无法控制对表的更新发生的位置,这就是为什么要实现触发器而不是只有一个proc set {{ 1}},你需要使用两种类型的触发器之一:

A)因此,如果您坚持使用“而不是”触发器,则需要了解替换可能发生的更新。因此,你有责任继续完成它的工作。您逐列检查已更改的内容:
e.g。

EditDate

...对每一列重复(如果有意义,可以合并更新)

B)或者您可以更改为“之后”触发器,允许更新发生(因此您不必逐列编码以检查已更新的内容)但是您必须检查{{ 1}}列,如果IF UPDATE (price) BEGIN UPDATE t SET price = i.price FROM TBL_LOCATIONS t join inserted i ON i.locn_id = t.locn_id END 列已更改,则不执行更新。如果你不这样做,你将处于一个无限循环 - 你的proc调用触发器调用触发器等

即。类似的东西:

EditDate