2011-10-17 20 views
5

¿Cómo consigo el valor del registro actualizado en un desencadenador de SQL - algo como esto:Disparadores SQL: ¿cómo obtengo el valor actualizado?

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

    EXEC UpdateProfile (SELECT UserId FROM updated AS U); 

END 
GO 

Obviamente esto no funciona, pero se puede ver lo que estoy tratando de llegar.

Respuesta

9

Proporcionar son cierto que sólo un valor nunca será actualizado, se puede hacer esto ...

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 

Si varios valores se pueden actualizar a la vez, sólo uno de los tres estará procesada por este código (Aunque no tendrá errores.)

Puede usar un cursor, o si se trata de SQL Server 2008+ puede usar variables de tabla.

O, más comúnmente, simplemente mueva el código StoredProcedure al disparador.

1

De acuerdo con mis conocimientos, necesitaría crear un CURSOR para recorrer todos los valores actualizados para ejecutar el procedimiento UpdateProfile. Tenga en cuenta que esto ralentizará su proceso de actualización.

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 

Mi sintaxis puede estar un poco desajustada, pero esto le dará el efecto deseado. Nuevamente, considere revisar su lógica para manejar múltiples actualizaciones ya que el uso de cursores consume muchos recursos.

0

Usted puede hacer algo como este ejemplo donde estoy registrar cambios en una tabla del historial de transacciones:

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 

Pero se puede utilizar el mismo tipo de técnica, se mezcla un poco y pasar todo el conjunto de valores a un procedimiento almacenado, como hago en el siguiente ejemplo (utilizando el esquema de tabla anterior).

En primer lugar, crear un procedimiento almacenado que espera una tabla temporal determinada a existir en tiempo de ejecución, por lo tanto:

-- 
-- 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 

Una vez que hayas hecho esto, crear un disparador que va a crear y poblar la tabla temporal esperado por el procedimiento almacenado y luego ejecutar el procedimiento almacenado:

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 

eso es todo lo que hay que hacer. Es simple, es fácil, funciona para conjuntos de cambios de cualquier tamaño. Y es seguro, sin condiciones de carrera o colisiones con otros usuarios en el sistema.

Cuestiones relacionadas