¿Cómo se realiza un disparador T-SQL que solo se dispara con cambios reales?

9

Tengo un activador de tabla en ACTUALIZAR e INSERTAR que agrega una fila a otra tabla. Solo necesita agregar una fila si se cambia una de las cuatro columnas. Intenté usar IF UPDATE (col) para probar los cambios, pero tiene un punto ciego. Solo prueba que entró algún valor. Necesito ir más profundo, necesito comparar los valores antiguos y nuevos para ver que se ha producido un verdadero cambio. Tiene que trabajar con INSERTAR y ACTUALIZAR.

En el caso de una ACTUALIZACIÓN, eso es fácil porque las tablas insertadas y eliminadas tienen valores que puedo comparar dentro del activador. Sin embargo, para INSERTAR solo la tabla de inserción tiene valores. Como necesito todo esto en el mismo disparador, ¿cómo manejo ese caso INSERT?

Aquí está la secuencia de comandos del desencadenador que quiero modificar:

ALTER TRIGGER [dbo].[trATPerson_alter] 
   ON  [mydb].[dbo].[AT_Person]
   AFTER INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    -- Not all updates require a push
    IF (UPDATE([First_Name]) OR UPDATE([Last_Name]) OR UPDATE([JobCode]) OR UPDATE([Inactive]))
    BEGIN
        INSERT INTO [mydb].[dbo].[AT_Person_To_Push] (
                [Facility],
                [VendorID],
                [Person_code],
                [First_Name],
                [Last_Name],
                [JobCode],
                [Alink],
                [Inactive]
            )
        SELECT  [Facility],
                [VendorID],
                [Person_code],
                [First_Name],
                [Last_Name],
                [JobCode],
                [Alink],
                [Inactive]
        FROM inserted 
    END
END
WillG
fuente
2
Una palabra rápida sobre el uso de "IF UPDATE (<column>)". Devuelve verdadero si el DML especifica un valor para la columna, independientemente de si el valor realmente cambió o no.
Jonathan Fite

Respuestas:

18

Puede manejar INSERTAR y ACTUALIZAR con un operador de conjunto EXCEPTO. EXISTS solo se evaluará como VERDADERO si es solo un INSERT, o si es una ACTUALIZACIÓN con valores diferentes para cualquiera de estas columnas.

IF EXISTS (
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM inserted
           EXCEPT
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM deleted
          )
BEGIN...
SQLRaptor
fuente
Esto es mucho más elegante que mirar las diversas funciones actualizadas por columnas. Combinamos aquellos con algún código de front-end para enviar solo los valores modificados (después de muchas disputas). Usar un EXCEPTO tiene mucho más sentido.
Peter Schott
2
Esto no funciona en los casos en que 2 filas se "intercambian" en una actualización. Si tenemos dos John Smiths que necesitan actualizar sus JobCodes (primero John de 1 a 2; segundo John de 2 a 1), esto diría que no se ha producido ninguna actualización.
Steven Hibble
2
@StevenHibble - Si bien es posible, ¿qué tan probable es que eso ocurra? Ese caso podría remediarse fácilmente mediante la inclusión de las columnas PK en las declaraciones de selección anteriores.
Chad Estes
1
Yo diría que la probabilidad depende de la fuente de datos y la probabilidad de una entrada de datos incorrecta. "Vaya, mal John Smith ..." no parece que nunca suceda. En cualquier caso, esto no aborda la otra mitad de una actualización de varias filas: ¿cómo se asegura de insertar solo las filas que cambian? Esto EXISTSverifica que cualquier fila haya cambiado. Si mantiene el inserto de la pregunta, luego registrará todas las filas actualizadas cuando solo una cambie de manera significativa.
Steven Hibble
2

En caso de que una actualización pueda afectar varias filas, debe protegerse contra dos cosas:

  1. Queremos considerar las actualizaciones que intercambian valores entre filas similares. Si hay dos John Smiths que necesitan actualizar sus JobCodes (primero John de 1 a 2; segundo John de 2 a 1), debemos tener cuidado de decir que ambos se actualizaron.
  2. Solo queremos registrar las filas modificadas AT_Person_To_Push. Si se actualizan 5 filas, pero solo 2 se actualizan de una manera que nos interesa, entonces necesitamos procesar solo las 2 filas relevantes.

Así es como lo manejaría:

  1. Izquierda unirse inserted a ladeleted , porque insertedtendrá filas para inserciones y actualizaciones, mientras deletedque solo tendrá filas para actualizaciones.
  2. Utilizar EXISTS con EXCEPTpara buscar filas donde los insertedvalores difieran de los deletedvalores. No se puede usar i.First_Name != d.First_Name OR i.Last_Name != d.Last_Name...porque la tabla eliminada estará vacía (y la IZQUIERDA IZQUIERDA devolverá nulos) cuando el activador maneja un INSERT.
  3. Inserte solo las filas afectadas en AT_Person_To_Push.
ALTER TRIGGER [dbo].[trATPerson_alter] 
   ON  [mydb].[dbo].[AT_Person]
   AFTER INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [mydb].[dbo].[AT_Person_To_Push] (
            [Facility],
            [VendorID],
            [Person_code],
            [First_Name],
            [Last_Name],
            [JobCode],
            [Alink],
            [Inactive]
        )
    SELECT  i.[Facility],
            i.[VendorID],
            i.[Person_code],
            i.[First_Name],
            i.[Last_Name],
            i.[JobCode],
            i.[Alink],
            i.[Inactive]
    FROM inserted i
         LEFT JOIN deleted d
           ON i.Person_code = d.Person_code
    -- Check for changes that require a push
    WHERE EXISTS (SELECT i.[First_Name], i.[Last_Name], i.[JobCode], i.[Inactive]
                  EXCEPT
                  SELECT d.[First_Name], d.[Last_Name], d.[JobCode], d.[Inactive]);
END
Steven Hibble
fuente
1

Prueba esto,

Declare @Acton int=0

If exists (Select 1 from inserted)
set @Acton=1

If exists (Select 1 from deleted)
set @Acton=@Acton+2

if(@Action=1) -- Only insert

if(@Action=3) -- Only Update
begin
IF (UPDATE([First_Name]) OR UPDATE([Last_Name]) OR UPDATE([JobCode]) OR UPDATE([Inactive]))
Begin

End
end

if(@Action=2) -- Only Delete
KumarHarsh
fuente