编写复杂的触发器

Posted

技术标签:

【中文标题】编写复杂的触发器【英文标题】:Writing a complex trigger 【发布时间】:2009-10-20 20:42:58 【问题描述】:

我正在使用 SQL Server 2000。我正在编写一个触发器,该触发器在字段申请者.AppStatusRowID 时执行

表申请者链接到表 Location、表 Company 和表 AppStatus。

我的问题是在我的查询中创建连接。

当申请人.AppStatusRowID 更新时,我想从中获取值 申请人.AppStatusRowID、申请人.FirstName、申请人.姓氏、Location.LocNumber、Location.LocationName、Company.CompanyCode、AppStatus.DisplayText

连接将是:

Select * from Applicant A
Inner Join AppStatus ast on ast.RowID = a.AppStatusRowID
Inner Join Location l on l.RowID = a.LocationRowID
Inner Join Company c on c.RowID = l.CompanyRowID

这将被插入到审核表中(字段为申请人 ID、姓氏、名字、日期、时间、公司、位置编号、位置名称、状态处置、用户)

我的问题是查询内连接...

【问题讨论】:

我希望你不要更新主键,这是个坏主意。 如果您更新密钥,您如何加入其他表? ast.RowID = a.AppStatusRowID 【参考方案1】:

首先让我们向您介绍仅在触发器中可用的插入和删除伪表。 Inserted 有新值,delted 有旧值或正在删除的记录。

您不想只将所有记录插入到您的审计表中。

因此,要插入审计表,您可能需要在触发器代码中添加类似内容:

insert Myaudittable (<insert field list here>)
Select <insert field list here> from Inserted i
Inner Join AppStatus ast on ast.RowID = i.AppStatusRowID
Inner Join Location l on l.RowID = i.LocationRowID
Inner Join Company c on c.RowID = l.CompanyRowID 

我会亲自添加新旧值的列、更改类型的列、更改的日期以及进行更改的用户,但您肯定有自己的要求要遵循。

建议您阅读在线图书中有关触发器的内容,因为它们可能很难正确处理。

这是我经常使用的一种测试和调试触发器的方法。首先,我创建了名为#delted 和#inserted 的临时表,它们具有我将要放置触发器的表的结构。然后我编写代码来使用那些而不是删除或插入的表。这样我就可以边看边看,确保一切都正确,然后再将代码更改为触发器。下面的示例添加了您的代码并稍作修改:

  Create table #inserted(Rowid int, lastname varchar(100), firstname varchar(100), appstatusRowid int)
  Insert #inserted
  select 1, 'Jones', 'Ed', 30
  union all
  select 2, 'Smith', 'Betty', 20

     Create table #deleted (Rowid int, lastname varchar(100), firstname varchar(100), appstatusRowid int)
  Insert #deleted
  select 1, 'Jones', 'Ed', 10
  union all
  select 2, 'Smith', 'Betty', 20

 --CREATE TRIGGER tri_UpdateAppDisp ON dbo.Test_App 
 --For Update 
 --AS 
 --If Update(appstatusrowid) 
 IF  exists (select i.appstatusRowid from #inserted i join #deleted d on i.rowid = d.rowid
            Where d.appstatusrowid <> i.appstatusrowid)
 BEGIN 
 --Insert AppDisp(AppID, LastName, FirstName, [DateTime],Company,Location,LocationName, StatusDisp,[Username]) 
 Select d.Rowid,d.LastName, d.FirstName, getDate(),C.CompanyCode,
 l.locnum,l.locname, ast.Displaytext, SUSER_SNAME()+' '+User 
 From #deleted d
 Join #inserted i on i.rowid = d.rowid
 --From deleted d 
 --Join inserted i on i.rowid = d.rowid
 Inner join Test_App a with (nolock) on a.RowID = d.rowid 
 inner join location l with (nolock) on l.rowid = d.Locationrowid 
 inner join appstatus ast  with (nolock) on ast.rowid = d.appstatusrowid 
 inner join company c with (nolock) on c.rowid = l.CompanyRowid
 Where d.appstatusrowid <> i.appstatusrowid)
 end

一旦你得到正确的选择数据,那么很容易取消注释掉触发代码和插入行并将#deleted或#inserted更改为删除或插入。

您会注意到我在临时表中有两条记录,一条满足您的条件,一条不满足。这允许您测试多个记录更新以及满足条件和不满足条件的结果。应该编写所有触发器来处理多条记录,因为它们不是逐行触发,而是按批次触发。

【讨论】:

不幸的是,我的创意有限。一旦有人更新了申请人表中的 AppStatusRowID,我需要将记录插入到审计表中。因此需要触发器。但我无法让我的联接查询工作。 那么你可能不得不使用 AppStatusRowID 的旧值,即参考 DELETED(而不是上面代码中的 Inserted)。 我尝试了 DELETED,但仍然没有为我工作。目标表未更新。 我一回到我的电脑就会发布它。 CREATE TRIGGER tri_UpdateAppDisp ON dbo.Test_App For Update AS If Update(appstatusrowid) BEGIN Insert AppDisp(AppID, LastName, FirstName, [DateTime],Company,Location,LocationName, StatusDisp,[Username]) 选择d.Rowid,d.LastName, d.FirstName, getDate(),C.CompanyCode,l.locnum,l.locname, ast.Displaytext, SUSER_SNAME()+' '+User From deleted d with(nolock) 内联Test_App a with (nolock) on a.RowID = d.rowid 内部连接位置 l with (nolock) on l.rowid = d.Locationrowid 内部连接 ​​appstatus ast with (nolock) on ast.rowid = d.appstatusrowid

以上是关于编写复杂的触发器的主要内容,如果未能解决你的问题,请参考以下文章

如何处理触发器中的错误?

SQL Server 存储过程和触发器

Oracle 触发器 触发器应用场景--复杂的安全性检查

在没有存储过程或触发器的情况下工作

存储过程、索引、视图、触发器 的区别

mysql 触发器 trigger用法 three (稍微复杂的)