SQL触发器 - 如何获取更新的值?

时间:2011-10-17 15:52:36

标签: sql sql-server tsql triggers

如何在SQL触发器中获取更新记录的值 - 如下所示:

CREATE TRIGGER TR_UpdateNew
   ON  Users
   AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    EXEC UpdateProfile (SELECT UserId FROM updated AS U);

END
GO

显然这不起作用,但你可以看到我想要的东西。

3 个答案:

答案 0 :(得分:11)

提供您某些只会更新一个值,您可以这样做...

CREATE TRIGGER TR_UpdateNew
   ON  Users
   AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    DECLARE @user_id INT
    SELECT
      @user_id = inserted.UserID
    FROM
      inserted
    INNER JOIN
      deleted
        ON inserted.PrimaryKey = deleted.PrimaryKey
        -- It's an update if the record is in BOTH inserted AND deleted

    EXEC UpdateProfile @user_id;

END
GO

如果可以同时更新多个值,则此代码只会处理其中一个值。 (虽然不会出错。)

您可以使用游标,或者如果它是SQL Server 2008+,您可以使用表变量。

或者,更常见的是,只需将StoredProcedure代码移动到触发器中即可。

答案 1 :(得分:2)

根据我的知识,您需要创建一个CURSOR来遍历所有更新的值以执行UpdateProfile过程。请记住,这会降低您的更新过程。

Declare @UserID int --Assuming
Declare UpdateProfile_Cursor Cursor for Select UserID From inserted;

Open Cursor UpdateProfile_Cursor;

Fetch Next from UpdateProfile_Cursor Into @UserID;

While @@FETCH_STATUS == 0
Begin
  Exec UpdateProfile  @UserID
  Fetch Next from UpdateProfile_Cursor Into @UserID;
End
CLOSE UpdateProfile_Cursor 
DEALLOCATE UpdateProfile_Cursor 

我的语法可能有些偏差,但这会给你带来理想的效果。同样,考虑修改逻辑以处理多个更新,因为使用游标是资源密集型的。

答案 2 :(得分:0)

您可以执行类似此示例的操作,我将更改记录到事务历史记录表中:

create table dbo.action
(
  id          int         not null primary key ,
  description varchar(32) not null unique ,
)
go

insert dbo.action values( 1 , 'insert' )
insert dbo.action values( 2 , 'update' )
insert dbo.action values( 3 , 'delete' )
go

create table dbo.foo
(
  id    int          not null identity(1,1) primary key ,
  value varchar(200) not null unique ,
)
go

create table dbo.foo_history
(
  id            int          not null ,
  seq           int          not null identity(1,1) ,
  action_date   datetime     not null default(current_timestamp) ,
  action_id     int          not null foreign key references dbo.action ( id ),
  old_value     varchar(200)     null ,
  new_value     varchar(200)     null ,

  primary key nonclustered ( id , seq ) ,

)
go

create trigger foo_update_01 on dbo.foo for insert, update , delete
as
  set nocount                 on
  set xact_abort              on
  set ansi_nulls              on
  set concat_null_yields_null on

  --
  -- record change history
  --
  insert dbo.foo_history
  ( 
    id        ,
    action_id ,
    old_value ,
    new_value
  )
  select id = coalesce( i.id , d.id ) ,
         action_id = case
                       when i.id is not null and d.id is     null then 1 -- insert
                       when i.id is not null and d.id is not null then 2 -- update
                       when i.id is     null and d.id is not null then 3 -- delete
                     end ,
         old_value = d.value ,
         new_value = i.value
  from      inserted i
  full join deleted  d on d.id = i.id

go

但是你可以使用相同的技术,将它混合一点并将整个值集传递给存储过程,就像我在下面的例子中所做的那样(使用上面的表模式)。

首先,创建一个存储过程,期望特定临时表在运行时存在,因此:

--
-- temp table must exist or the stored procedure won't compile
--
create table #foo_changes
(
  id        int          not null primary key clustered ,
  action_id int          not null ,
  old_value varchar(200)     null ,
  new_value varchar(200)     null ,
)
go
--
-- create the stored procedure
--
create procedure dbo.foo_changed
as

  --
  -- do something useful involving the contents of #foo_changes here
  --
  select * from #foo_changes

  return 0
go
--
-- drop the temp table
--
drop table #foo_changes
go

完成后,创建一个触发器,创建并填充存储过程所需的临时表,然后执行存储过程:

create trigger foo_trigger_01 on dbo.foo for insert, update , delete
as
  set nocount                 on
  set xact_abort              on
  set ansi_nulls              on
  set concat_null_yields_null on

  --
  -- create the temp table. This temp table will be in scope for any stored
  -- procedure executed by this trigger. It will be automagickally dropped
  -- when trigger execution is complete.
  --
  -- Any changes made to this table by a stored procedure — inserts,
  -- deletes or updates are, of course, visible to the trigger upon return
  -- from the stored procedure.
  --
  create table #foo_changes
  (
    id        int          not null primary key clustered ,
    action_id int          not null ,
    old_value varchar(200)     null ,
    new_value varchar(200)     null ,
  )

  --
  -- populate the temp table
  --
  insert #foo_changes
  ( 
    id        ,
    action_id ,
    old_value ,
    new_value
  )
  select id = coalesce( i.id , d.id ) ,
         action_id = case
                       when i.id is not null and d.id is     null then 1 -- insert
                       when i.id is not null and d.id is not null then 2 -- update
                       when i.id is     null and d.id is not null then 3 -- delete
                     end ,
         old_value = d.value ,
         new_value = i.value
  from      inserted i
  full join deleted  d on d.id = i.id

  --
  -- execute the stored procedure. The temp table created above is in scope
  -- for the stored procedure, so it's able to access the set of changes from
  -- the trigger.
  --
  exec dbo.foo_changed

go

这就是它的全部内容。它很简单,很容易,适用于任何大小的变更集。并且,它是安全的,没有竞争条件或与系统中的其他用户发生冲突。