Aquí tienes una forma sencilla de hacer esto:
Primero, cree una tabla de historial para cada tabla de datos que desee rastrear (consulta de ejemplo a continuación). Esta tabla tendrá una entrada para cada consulta de inserción, actualización y eliminación realizada en cada fila de la tabla de datos.
La estructura de la tabla de historial será la misma que la tabla de datos que rastrea excepto por tres columnas adicionales: una columna para almacenar la operación que ocurrió (llamémosla 'acción'), la fecha y hora de la operación y una columna para almacenar un número de secuencia ('revisión'), que se incrementa por operación y se agrupa por la columna de clave principal de la tabla de datos.
Para realizar este comportamiento de secuenciación, se crea un índice de dos columnas (compuesto) en la columna de clave principal y la columna de revisión. Tenga en cuenta que solo puede realizar la secuenciación de esta manera si el motor utilizado por la tabla de historial es MyISAM ( consulte 'Notas de MyISAM' en esta página)
La tabla de historial es bastante fácil de crear. En la consulta ALTER TABLE a continuación (y en las consultas de activación debajo de eso), reemplace 'primary_key_column' con el nombre real de esa columna en su tabla de datos.
CREATE TABLE MyDB.data_history LIKE MyDB.data;
ALTER TABLE MyDB.data_history MODIFY COLUMN primary_key_column int(11) NOT NULL,
DROP PRIMARY KEY, ENGINE = MyISAM, ADD action VARCHAR(8) DEFAULT 'insert' FIRST,
ADD revision INT(6) NOT NULL AUTO_INCREMENT AFTER action,
ADD dt_datetime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER revision,
ADD PRIMARY KEY (primary_key_column, revision);
Y luego creas los disparadores:
DROP TRIGGER IF EXISTS MyDB.data__ai;
DROP TRIGGER IF EXISTS MyDB.data__au;
DROP TRIGGER IF EXISTS MyDB.data__bd;
CREATE TRIGGER MyDB.data__ai AFTER INSERT ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'insert', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
CREATE TRIGGER MyDB.data__au AFTER UPDATE ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'update', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
CREATE TRIGGER MyDB.data__bd BEFORE DELETE ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'delete', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = OLD.primary_key_column;
Y tu estas listo. Ahora, todas las inserciones, actualizaciones y eliminaciones en 'MyDb.data' se registrarán en 'MyDb.data_history', brindándole una tabla de historial como esta (menos la columna artificial 'data_columns')
ID revision action data columns..
1 1 'insert' .... initial entry for row where ID = 1
1 2 'update' .... changes made to row where ID = 1
2 1 'insert' .... initial entry, ID = 2
3 1 'insert' .... initial entry, ID = 3
1 3 'update' .... more changes made to row where ID = 1
3 2 'update' .... changes made to row where ID = 3
2 2 'delete' .... deletion of row where ID = 2
Para mostrar los cambios para una columna o columnas determinadas de actualización a actualización, deberá unir la tabla de historial a sí misma en la clave principal y las columnas de secuencia. Puede crear una vista para este propósito, por ejemplo:
CREATE VIEW data_history_changes AS
SELECT t2.dt_datetime, t2.action, t1.primary_key_column as 'row id',
IF(t1.a_column = t2.a_column, t1.a_column, CONCAT(t1.a_column, " to ", t2.a_column)) as a_column
FROM MyDB.data_history as t1 INNER join MyDB.data_history as t2 on t1.primary_key_column = t2.primary_key_column
WHERE (t1.revision = 1 AND t2.revision = 1) OR t2.revision = t1.revision+1
ORDER BY t1.primary_key_column ASC, t2.revision ASC
Editar: Oh wow, a la gente le gusta mi tabla de historial de hace 6 años: P
Mi implementación sigue avanzando, haciéndose más grande y más difícil de manejar, supongo. Escribí vistas y una interfaz de usuario bastante agradable para ver el historial en esta base de datos, pero no creo que se haya usado mucho. Así que va.
Para abordar algunos comentarios sin ningún orden en particular:
Hice mi propia implementación en PHP que fue un poco más complicada y evité algunos de los problemas descritos en los comentarios (tener índices transferidos, significativamente. Si transfieres índices únicos a la tabla de historial, las cosas se romperán. Hay soluciones para esto en los comentarios). Seguir esta publicación al pie de la letra podría ser una aventura, dependiendo de qué tan establecida esté tu base de datos.
Si la relación entre la clave principal y la columna de revisión parece incorrecta, generalmente significa que la clave compuesta está bloqueada de alguna manera. En algunas raras ocasiones me sucedió esto y no sabía la causa.
Encontré que esta solución es bastante eficaz, utilizando desencadenadores como lo hace. Además, MyISAM es rápido en inserciones, que es todo lo que hacen los desencadenantes. Puede mejorar esto aún más con la indexación inteligente (o la falta de ...). Insertar una sola fila en una tabla MyISAM con una clave principal no debería ser una operación que deba optimizar, en realidad, a menos que tenga problemas importantes en otro lugar. Durante todo el tiempo que estuve ejecutando la base de datos MySQL en la que estuvo la implementación de la tabla de historial, nunca fue la causa de ninguno de los (muchos) problemas de rendimiento que surgieron.
si recibe inserciones repetidas, verifique su capa de software para las consultas de tipo INSERT IGNORE. Mmmm, no puedo recordar ahora, pero creo que hay problemas con este esquema y transacciones que finalmente fallan después de ejecutar múltiples acciones DML. Algo a tener en cuenta, al menos.
Es importante que los campos de la tabla de historial y la tabla de datos coincidan. O, más bien, que su tabla de datos no tiene MÁS columnas que la tabla de historial. De lo contrario, las consultas de inserción / actualización / eliminación en la tabla de datos fallarán cuando las inserciones en las tablas del historial coloquen columnas en la consulta que no existen (debido a d. * En las consultas de activación) y el activador falla. Sería increíble si MySQL tuviera algo como activadores de esquema, donde podría alterar la tabla de historial si se agregan columnas a la tabla de datos. ¿MySQL tiene eso ahora? Reacciono estos días: P