Activador de actualización de SQL solo cuando se modifica la columna

101

Al observar otros ejemplos, se me ocurrió lo siguiente, pero no parece funcionar como me gustaría: quiero que solo actualice la información modificada si el QtyToRepairvalor se ha actualizado ... pero no funciona ese.

Si comento dónde, la información modificada se actualiza en todos los casos. Como dije, otros ejemplos me llevaron a ser optimista. Cualquier pista apreciada. Gracias.

Walter

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    UPDATE SCHEDULE SET modified = GETDATE()
        , ModifiedUser = SUSER_NAME()
        , ModifiedHost = HOST_NAME()
    FROM SCHEDULE S
    INNER JOIN Inserted I on S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
    WHERE S.QtyToRepair <> I.QtyToRepair
END
Walter de Jong
fuente
8
Una advertencia sobre update(): solo prueba si la columna aparece en la lista de actualización y siempre es cierta para las inserciones. No comprueba si el valor de la columna ha cambiado, porque es posible que tenga más de una fila, donde algunos valores han cambiado y otros no.
Nikola Markovinović

Respuestas:

128

Tiene dos vías para su pregunta:

1- Usa el comando de actualización en tu gatillo.

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS BEGIN
    SET NOCOUNT ON;
    IF UPDATE (QtyToRepair) 
    BEGIN
        UPDATE SCHEDULE 
        SET modified = GETDATE()
           , ModifiedUser = SUSER_NAME()
           , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S INNER JOIN Inserted I 
        ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END 
END

2- Use Unir entre la tabla insertada y la tabla eliminada

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS BEGIN
    SET NOCOUNT ON;    

    UPDATE SCHEDULE 
    SET modified = GETDATE()
       , ModifiedUser = SUSER_NAME()
       , ModifiedHost = HOST_NAME()
    FROM SCHEDULE S 
    INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
    INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber                  
    WHERE S.QtyToRepair <> I.QtyToRepair
    AND D.QtyToRepair <> I.QtyToRepair
END

Cuando usa el comando de actualización para la tabla SCHEDULEy establece la QtyToRepaircolumna en un nuevo valor, si el nuevo valor es igual al valor anterior en una o varias filas, la solución 1 actualiza todas las filas actualizadas en la tabla de programación, pero la solución 2 solo actualiza las filas de programación que el valor anterior no es igual al nuevo valor.

mehdi lotfi
fuente
Perdón por no mencionar que era SQLServer. Necesitaba usar la tabla eliminada. Terminé escribiendo en una mesa separada (para mantener un historial).
Walter de Jong
9
El enfoque n. ° 2 es mejor si no desea que su disparador se dispare si la columna se cambia al mismo valor.
Neolisk
2
Creo que me puede faltar algo, pero el enfoque n. ° 1 no funcionaría, porque esto es después de la actualización, por lo que la fila actual siempre tendrá el mismo valor que la fila de inserción. Usted debe unirse a borrar si está usando después de la actualización
Rob
5
@Rob La declaración "IF UPDATE" invoca un procedimiento que tiene acceso a una lista de columnas que se actualizaron como parte de la consulta y, por lo tanto, no necesita conocer los valores anteriores. (Nos encontramos con esto en nuestro entorno y confirmamos que "IF UPDATE" se cumple independientemente de la cláusula "DESPUÉS DE ACTUALIZAR")
TChadwick
3
¿Por qué se une a la insertedtabla en la segunda consulta? debería ser igual que la mesa misma, ¿no?
teran
22

fyi El código con el que terminé:

IF UPDATE (QtyToRepair)
    begin
        INSERT INTO tmpQtyToRepairChanges (OrderNo, PartNumber, ModifiedDate, ModifiedUser, ModifiedHost, QtyToRepairOld, QtyToRepairNew)
        SELECT S.OrderNo, S.PartNumber, GETDATE(), SUSER_NAME(), HOST_NAME(), D.QtyToRepair, I.QtyToRepair FROM SCHEDULE S
        INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
        INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber 
        WHERE I.QtyToRepair <> D.QtyToRepair
end
Walter de Jong
fuente
1
Encontré esto útil porque la respuesta 2- Use Join between anterior nunca funcionó debido a que los criterios WHERE S.QtyToRepair <> I.QtyToRepair AND D.QtyToRepair <> I.QtyToRepairnunca se activaron / coincidieron, ya que el primer criterio nunca fue cierto: la tabla insertada siempre coincidió con el valor real de la tabla. Usar `WHERE I.QtyToRepair <> D.QtyToRepair` fue la clave para mí. También la IF UPDATE (field)ayuda de múltiples disparadores.
Firegarden
13

Uno debe verificar si QtyToRepairestá actualizado al principio.

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
SET NOCOUNT ON;
    IF UPDATE (QtyToRepair) 
    BEGIN
        UPDATE SCHEDULE 
        SET modified = GETDATE()
           , ModifiedUser = SUSER_NAME()
           , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S INNER JOIN Inserted I 
            ON S.OrderNo = I.OrderNo and S.PartNumber =    I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END
END
hgulyan
fuente
6

Quieres hacer lo siguiente:

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
SET NOCOUNT ON;

    IF (UPDATE(QtyToRepair))
    BEGIN
        UPDATE SCHEDULE SET modified = GETDATE()
            , ModifiedUser = SUSER_NAME()
            , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S
        INNER JOIN Inserted I ON S.OrderNo = I.OrderNo AND S.PartNumber = I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END
END

Tenga en cuenta que este activador se activará cada vez que actualice la columna, sin importar si el valor es el mismo o no.

Carlos Vergara
fuente
2

Siempre que un registro se actualiza, se "elimina". Aquí está mi ejemplo:

ALTER TRIGGER [dbo].[UpdatePhyDate]
   ON  [dbo].[M_ContractDT1]
   AFTER UPDATE
AS 
BEGIN
    -- on ContarctDT1 PhyQty is updated 
    -- I want system date in Phytate automatically saved
    SET NOCOUNT ON;

    declare @dt1ky as int   

    if(update(Phyqty))
    begin
        select @dt1ky = dt1ky from deleted

        update M_ContractDT1 set PhyDate=GETDATE() where Dt1Ky=  @dt1ky     

    end

END

Funciona bien

Mohamed Yazeer
fuente