2012-07-24 10 views
5

Estoy escribiendo un disparador para realizar un seguimiento de todos los cambios que ocurren en una tabla. Lamentablemente, la tabla tiene más de 150 columnas y quería evitar escribir cada columna en el código (por ejemplo, new.col1, new.col2 ....) y así escribí una consulta siguiente en "después de activar la actualización"mysql Desencadenante para iniciar sesión, busque las columnas modificadas

INSERT INTO logs SELECT *, NOW() FROM abc WHERE abc.id = NEW.Id; 

Esta idea está causando un problema múltiple debido a la duplicación de datos que no se cambia en la consulta de actualización.

En pocas palabras, quiero saber dinámicamente qué columnas formaban parte de la consulta de actualización y si eso no es posible, hay una manera de recorrer todas las columnas de la fila "nueva" para poder comparar dinámicamente las antiguas @. colName == new. @ colName?

Ya he visto Oracle PL/SQL: Loop Over Trigger Columns Dynamically, How to determine if anything changed in update trigger in t-sql y MySQL UPDATE trigger: INSERTing the values of the columns that actually changed.

El último enlace es el que cierra con lo que necesito con solo una diferencia, no quiero codificar los nombres de las columnas en la siguiente declaración porque tengo más de 100 columnas en todas las tablas que voy a escribir similar desencadenar !!

IF NEW.column1 <> OLD.column1 THEN INSERT INTO... END IF; IF NEW.column2 <> OLD.column2 THEN INSERT INTO... END IF 

Respuesta

2

que he estado haciendo un poco de investigación en este esta mañana y parece que me he encontrado con muchos de los mismos resultados de búsqueda como usted. En última instancia, me parece que no hay forma de recorrer todas las columnas de la tabla y hacer referencia a los valores antiguos/nuevos correspondientes. Estoy de decidirse por la comprobación de forma explícita cada columna y luego iniciar sesión:

IF (NEW.fld1 <> OLD.fld1) OR (NEW.fld1 IS NOT NULL AND OLD.fld1 IS NULL) OR (NEW.fld1 IS NULL AND OLD.fld1 IS NOT NULL) THEN 
INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`) 
VALUES ("tblname", "fld1", OLD.fld1, NEW.fld1); 
END IF; 

IF (NEW.fld2 <> OLD.fld2) OR (NEW.fld2 IS NOT NULL AND OLD.fld2 IS NULL) OR (NEW.fld2 IS NULL AND OLD.fld2 IS NOT NULL) THEN 
INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`) 
VALUES ("tblname", "fld2", OLD.fld2, NEW.fld2); 
END IF; ... 

He encontrado un indicio de otra solución here. En teoría, podría tener 3 listas delimitadas, una para los nombres de columna, una para vals antiguos y otra para vals nuevos. Tendría que hacer referencia explícita a los valores vals antiguo y nuevo, pero eso sería una línea (más fácil de mantener o copiar/pegar para implementar en otras tablas) y podría hacer un ciclo. Por lo tanto, en el pseudo código se vería algo como esto:

fields_array = concat_ws(",", "fld1", "fld2"); 
old_vals_array = concat_ws(",", OLD.fld1, OLD.fld2); 
new_vals_array = concat_ws(",", NEW.fld1, NEW.fld2); 

foreach fields_array as key => field_name 
    INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`) 
    VALUES ("tblname", field_name, old_vals_array[key], vew_vals_array[key]); 

No lo he pensado demasiado. Es posible que necesite llamar a un procedimiento almacenado en lugar de establecer variables. Pero podría valer la pena investigarlo. Ya he dedicado suficiente tiempo a mis disparadores. No estoy seguro de poder validar (a mi jefe) el tiempo de prueba y error en una solución más elegante.

+0

He estado enfrenta ahora a un nuevo tipo de problema ... Incluso si estoy listo para codificar todos mis 150 columnas en mis disparadores, estoy teniendo ¡es un mal momento para mantener un registro de la columna "UpdatedBy"! Mi tabla original realiza un seguimiento de las personas que iniciaron sesión en la aplicación y que hizo el último cambio a través de la aplicación. Lamentablemente, nuestra tecnología de soporte técnico puede cambiar datos directamente desde la base de datos sin actualizar la columna "updatedBy" y en ese caso no hay manera de saber si "updatedBy" fue parte de la consulta o no y por lo tanto no puedo configurar esa columna ¡anular cuando sea necesario! Espero que tuviera sentido aquí :) – Sap

+0

Tiene sentido. Sin embargo, no puedo hacer nada al respecto. Esto es para lo que son las API y las "Interfaces". Para obligar a la gente de "apoyo"/otros desarrolladores a hacer lo que se supone que deben hacer. – Jegsar

1

Como ya se ha sugerido, sugerido, puede usar CONCAT_WS para hacer cadenas de todos los valores requeridos y hacer una única declaración de comparación.

Tal vez el siguiente es útil para alguien:

DECLARE old_concat, new_concat text; 
SET old_concat = CONCAT_WS(',', OLD.fld1, OLD.fld2, ...); 
SET new_concat = CONCAT_WS(',', NEW.fld1, NEW.fld2, ...); 

IF old_concat <> new_concat 
THEN 
    INSERT STATEMENT 
END IF; 
Cuestiones relacionadas