2010-09-20 779 views
12

Por ejemplo, tengo una tabla que almacena detalles sobre las propiedades. Que podría tener propietarios, valor, etc.¿Cuál es la mejor manera de mantener el historial de cambios en los campos de la base de datos?

¿Hay un buen diseño para mantener el historial de cada cambio de propietario y valor? Quiero hacer esto para muchas tablas. Algo así como una auditoría de la mesa.

lo que pensaba era mantener una sola tabla con los campos

table_name, field_name, prev_value, current_val, time, user.

Pero parece un poco hacky y feo. ¿Hay un mejor diseño?

Gracias.

Respuesta

21

Hay algunos enfoques

campo basado

audit_field (table_name, id, field_name, field_value, datetime) 

Éste puede capturar la historia de todas las tablas y es fácil de ampliar a nuevas tablas. No se necesitan cambios en la estructura para las nuevas tablas.

Field_value a veces se divide en varios campos para admitir de forma nativa el tipo de campo real de la tabla original (pero solo uno de esos campos se rellenará, por lo que los datos se desnormalizan; una variante consiste en dividir la tabla anterior en una tabla para cada tipo).

Otros metadatos como field_type, user_id, user_ip, action (update, delete, insert) etc. pueden ser útiles.

La estructura de tales registros muy probablemente tendrá que ser transformada para ser utilizada.

registro basado

audit_table_name (timestamp, id, field_1, field_2, ..., field_n) 

Para cada tipo de registro en la base de datos crear una tabla generalizada que tiene todos los campos que el registro original, además de un campo de control de versiones (metadatos adicionales posibles de nuevo). Se necesita una tabla para cada mesa de trabajo. El proceso de crear tales tablas puede ser automatizado.

Este enfoque le proporciona una estructura semánticamente rica muy similar a la estructura de datos principal por lo que las herramientas utilizadas para analizar y procesar los datos originales también se pueden usar fácilmente en esta estructura.

archivo de registro

Los dos primeros enfoques suelen utilizar tablas que están indexados muy ligeramente (o ningún índice en absoluto y sin integridad referencial) de manera que se reduzca al mínimo la pena escribir. Sin embargo, a veces se prefiere el archivo de registro plano, pero, por supuesto, se reduce considerablemente. (Básicamente depende de si desea una auditoría/registro real que será analizado por algún otro sistema o los registros históricos son la parte del sistema principal).

+0

esto parece completo. Me pregunto qué rendimiento sería para cada enfoque. Entonces, en base a registros, habrá una tabla espejo (en términos de campos) con un campo de versión? – saint

+1

@saint, el rendimiento es un compuesto, depende de los patrones de uso. El archivo de registro será el más rápido de anotar (especialmente si es una partición separada o un nodo separado). Y seguro estarás escribiendo mucho. Ahora, ¿qué tan importante es leer? Si va a leer una vez en una semana, entonces es algo diferente comparado con la inspección de la correlación de la línea de tiempo entre varios campos o haciendo alguna otra extracción de datos (como un ejemplo extremo). – Unreason

+0

@saint, sí, en base a registros extiende las tablas con el campo de control de versiones (y posiblemente otros metadatos interesantes como el usuario). – Unreason

4

En nuestros proyectos por lo general hacerlo de esta manera: Tiene una tabla

properties(ID, value1, value2) 

a continuación, agregar la tabla

properties_audit(ID, RecordID, timestamp or datetime, value1, value2) 

ID -es un id de registro de la historia (no es realmente necesario)

RecordID -marca el registro en la tabla de propiedades originales.

cuando actualiza la tabla properties agrega un nuevo registro a properties_audit con los valores previos del registro actualizado en properties. Esto se puede hacer usando desencadenantes o en su DAL.

Después de eso, tiene el último valor en properties y todo el historial (valores previos) en properties_audit.

2

creo un esquema más simple sería

table_name, field_name, value, time, userId 

No hay necesidad de guardar los valores actuales y anteriores en las tablas de auditoría. Cuando realiza un cambio en cualquiera de los campos, solo tiene que agregar una fila en la tabla de auditoría con el valor modificado. De esta manera, siempre puede ordenar la tabla de auditoría a tiempo y saber cuál fue el valor anterior en el campo antes de su cambio.

+0

tienes razón, parece innecesario. Simplemente ingresé sin pensar. – saint

+1

solo para enfatizar algunos puntos malos de este esquema (que pueden aplicarse o no). El uso de la información de esta tabla en las tablas de atributos ricos es generalmente un dolor en el cuello. Si desea mostrar sus datos, deberá convertir los valores a los tipos apropiados, luego recopilar todos los atributos y básicamente convertirlos en una estructura que se asemeje al registro original (depende del uso, pero probablemente sea cierto como regla). Si no considera esto como un problema (digamos que automatiza el cliente), entonces no son tablas detalladas, ya que puede automatizar el mantenimiento de esas (tablas de creación y disparadores con un script) – Unreason

6

Una forma diferente de ver esto es dimensionar el tiempo de los datos.

Asumiendo que su mesa se ve así:

create table my_table (
my_table_id  number  not null primary key, 
attr1   varchar2(10) not null, 
attr2   number   null, 
constraint my_table_ak unique (attr1, att2)); 

Entonces, si la ha cambiado, así:

create table my_table (
my_table_id  number  not null, 
attr1   varchar2(10) not null, 
attr2   number   null, 
effective_date date   not null, 
is_deleted  number(1,0) not null default 0, 
constraint my_table_ak unique (attr1, att2, effective_date) 
constraint my_table_pk primary key (my_table_id, effective_date)); 

Usted sería capaz de tener una historia completa de correr mi_tabla, en línea y disponible . Tendría que cambiar el paradigma de los programas (o usar activadores de base de datos) para interceptar la actividad ACTUALIZAR en la actividad INSERTAR y para cambiar la actividad ELIMINAR en ACTUALIZAR el booleano IS_DELETED.


Unreason:

Tiene razón en que esta solución similar para grabar basada en la auditoría; Lo leí inicialmente como una concatenación de campos en una cadena, que también he visto. Mis disculpas.

Las principales diferencias que veo entre el dimensionamiento temporal de la tabla y el uso de un centro de auditoría basado en registros en torno a la mantenibilidad sin sacrificar el rendimiento ni la escalabilidad.

Mantenibilidad: Hay que recordar cambiar la tabla de sombras si se realiza un cambio estructural en la tabla primaria. Del mismo modo, es necesario recordar realizar cambios en los desencadenantes que realizan el seguimiento de cambios, ya que dicha lógica no puede vivir en la aplicación. Si uno utiliza una vista para simplificar el acceso a las tablas, también debe actualizarla y cambiar el activador en lugar de activar interceptar DML.

En una tabla de dimensiones de tiempo, realiza el cambio estructural que necesita, y listo. Como alguien que ha sido FNG en un proyecto heredado, se aprecia esa claridad, especialmente si tiene que hacer muchas refacciones.

Rendimiento y escalabilidad: Si se divide una tabla de tiempo en la columna de fecha de vigencia/caducidad, los registros activos están en una "tabla" y los registros inactivos están en otra. ¿Exactamente cómo es eso menos escalable que su solución? La "eliminación" y el registro activo implican el movimiento de la fila en Oracle, que es una eliminación e inserción debajo de las cubiertas, exactamente lo que la solución basada en registros requeriría.

La otra cara del rendimiento es que si la aplicación está buscando un registro a partir de una fecha determinada, la eliminación de partición permite que la base de datos busque solo la tabla/índice donde podría estar el registro; una solución basada en vistas para buscar registros activos e inactivos requeriría UNION-ALL, y no usar dicha vista requiere poner UNION-ALL en todas partes, o usar algún tipo de lógica "look-here, then look-there" en la aplicación, a la que digo: blech.

En resumen, es una opción de diseño; No estoy seguro de que tenga razón o que ninguno esté equivocado.

+0

Utilice una vista para obtener solo los activos. Otro problema con este enfoque es que las tablas pueden ser muy grandes, muy rápidas. Tendrás que ser bueno en la optimización del rendimiento. – HLGEM

+0

Sin embargo, si el historial necesita ser * fácilmente * accesible, esto es mucho más fácil que descifrar las opciones de auditoría basadas en campo o basadas en registros y elimina la desagradable complejidad de recordar de qué tablas tiene sombras, como refactorización de uno requiere refactorización del otro. (Particionar un 'expiry_date' en su lugar, usando NULL para indicar un registro actual, elimina la necesidad de una vista.) –

+0

esto se ve limpio, no estoy seguro de entender el uso de la fecha de vigencia única? – saint

Cuestiones relacionadas