2009-04-03 11 views
9

Como en muchas bases de datos, estoy diseñando una base de datos que debe mantener el registro de las versiones anteriores de las filas modificadas en cada tabla.historial de administración de filas en la base de datos

La solución estándar a este problema es mantener una tabla de historial para cada tabla de datos, y cada vez que se necesita actualizar una fila en la tabla de datos, una copia de la fila actual se inserta en la tabla de historial y luego fila en la tabla de datos se actualiza.

las desventajas de esta solución para mí:

  • mantenimiento de 2 mesas en lugar de 1, (en caso de que la estructura de las necesidades de la tabla modificar)
  • la aplicación necesita saber tanto de las mesas en vez uno de
  • nombres de las tablas que tenga que ser cortas para evitar una convención del nombre de la tabla y el nombre de tabla de historial (some_table, SOME_TABLE_HIST por ejemplo)

que una Estoy considerando una solución diferente y me gustaría saber si está bien. para cada tabla, se añade la columna IS_LAST

  • cuando una fila se inserta a la mesa, que conseguirá insertada con IS_LAST = 1.
  • cuando se actualiza una fila, una copia de la fila original se duplicará en la misma tabla con el cambio de IS_LAST = 0, y la fila original se actualizará según sea necesario (manteniendo IS_LAST = 1).

supongo que en mi caso, las filas se actualizan a un promedio de 10 veces. también asumen que al menos el 90% de las acciones realizadas por la aplicación solo ocurre en la versión reciente de las filas.

mi base de datos es un Oracle 10g por lo que para mantener la tabla "activa" delgada, podemos dividir la tabla en 2 particiones: la partición IS_LAST = 1 y la partición IS_LAST = 0.

¿Las particiones son una buena forma de resolver el problema del mantenimiento de datos históricos?

¿Esta solución limita otro potencial de partición a estas tablas?

gracias!

Respuesta

2

que crearía dos tablas: una para IsLast tipo de valores y uno de los históricos. Luego configuraría un disparador que insertara valor en la tabla histórica cada vez que se actualice isLast.

+3

Es Oracle, ¿para qué molestarse? Solo particione en esa columna y active Migración de fila. Está integrado, por qué reescribir y mantener dos tablas. –

0

La principal limitación que me viene a la mente es que una parte sustancial de su tabla será de datos de historial, lo que significa preocupaciones de indexación y potencialmente la introducción de complejidad adicional en sus consultas CRUD.

¿Hay alguna razón en particular que usted no desea utilizar lo que parece ser la solución habitual a esta situación?

+0

Es Oracle, la partición resuelve su problema. Simplemente particiones en Is_last y activa la migración de filas y listo, tus consultas de que is_last = 1 nunca verán los datos antiguos. –

0

¿Cómo se definirán las claves principales? Habrá muchas filas con la misma clave principal debido a la colocación de las filas del historial en la misma tabla.

También parece que no tiene una forma de saber el orden de sus filas históricas cuando una única fila "real" se cambia más una vez.

(Uno de los proyectos que trabajé, que genera todas las tablas de la historia y los factores desencadenantes utilizando CodeSmith, esto funcionó muy bien.)

6

primera pregunta debería ser: ¿qué hacer con esos datos? Si no tiene un requisito comercial claro, no lo haga.

Hice algo similar y después de 3 años de funcionamiento, hay alrededor del 20% de "datos válidos" y el resto es "versiones anteriores". Y son 10 millones + 40 millones de registros. En los últimos tres años tuvimos 2 (dos) solicitudes para investigar el historial de cambios y las dos veces las solicitudes fueron tontas: registramos la marca de tiempo del cambio de registro y se nos solicitó verificar si las personas trabajaron horas extras (después de las 5pm).

Ahora, estamos atrapados con una base de datos de gran tamaño que contiene el 80% de los datos que nadie necesita.

EDIT:

Has solicitado que las posibles soluciones, voy a describir lo que hicimos. Es un poco diferente de la solución que está considerando.

  1. Todas las tablas tienen clave primaria sustituta.
  2. Todas las claves principales se generan a partir de una sola secuencia. Esto funciona bien porque Oracle puede generar y almacenar en caché los números, por lo que no hay problemas de rendimiento aquí. Usamos ORM y deseamos que cada objeto en la memoria (y el registro correspondiente en la base de datos) tenga el identificador único
  3. Usamos ORM y la información de mapeo entre la tabla de la base de datos y la clase está en forma de atributos.

Registramos todos los cambios en la tabla de archivo individual con columnas siguientes:

  • id (clave principal sustituto)
  • sello de tiempo
  • tabla original
  • id del registro original
  • ID de usuario
  • Tipo de transacción (insertar, actualizar, eliminar)
  • datos de registro como varchar2 campo
    • esto es datos reales en forma de pares nombre de campo/valor.

cosa funciona de esta manera:

  • ORM tiene insertar/actualizar y eliminar comands.
  • hemos creado una clase base para todos nuestros objetos de negocio que anula insertar/actualizar y eliminar comandos
    • insertar/actualizar/borrar comandos crean cadena en forma de pares de nombre de campo/valor utilizando la reflexión. El código busca la información de asignación y lee el nombre del campo, el valor asociado y el tipo de campo. Luego creamos algo similar a JSON (añadimos algunas modificaciones). Cuando se crea una cadena que representa el estado actual del objeto, se inserta en la tabla de archivo.
  • cuando el objeto nuevo o actualizado se guarda en la tabla de la base de datos, se guarda en su tabla de destino y al mismo tiempo insertamos un registro con el valor actual en la tabla de archivo.
  • cuando se elimina objeto, los borramos de la tabla de destino y, al mismo tiempo que insertar un registro en la tabla de archivos que tiene el tipo de transacción = "BORRAR"

Pro:

  • nos no tiene tablas de archivo para cada tabla en la base de datos. Tampoco tenemos que preocuparnos de actualizar la tabla de archivos cuando cambia el esquema.
  • archivo completo está separado de "datos actuales", por lo que el archivo no impone ningún golpe de rendimiento en la base de datos. Lo colocamos en tablespace en un disco separado y funciona bien.
  • hemos creado 2 formas de archivo de visualización:
    • espectador general que puede enumerar tabla de archivo de acuerdo con filtro en la tabla de archivos. El usuario de datos de filtro puede ingresar en el formulario (lapso de tiempo, usuario, ...). Mostramos cada registro en nombre/nombre de campo de formulario y cada cambio tiene un código de color. Los usuarios pueden ver todas las versiones de cada registro y pueden ver quién y cuándo realizó los cambios.
    • visor de facturas: este fue complejo, pero creamos un formulario que muestra una factura muy similar al formulario de entrada de factura original, pero con algunos botones adicionales que pueden mostrar diferentes generaciones. Tomó un esfuerzo considerable para crear esta forma. La forma se usó pocas veces y luego se olvidó porque no era necesaria en el flujo de trabajo actual.
  • código para crear registros de archivo se encuentra en la clase única C#. No hay necesidad de desencadenantes en cada tabla de la base de datos.
  • el rendimiento es muy bueno. En las horas punta, el sistema es utilizado por alrededor de 700-800 usuarios. Esta es la aplicación ASP.Net. Tanto ASP.Net como Oracle se ejecutan en un XEON dual con 8 Gb de RAM.

Contras:

  • formato de archivo único tabla es difícil de leer que solución en la que hay una tabla de archivo para cada una de las tablas de datos.
  • búsqueda en el campo sin identificación en la tabla de archivos es difícil - podemos usar solo el operador LIKE en la cadena.

Así, de nuevo, comprobar los requisitos de archivo. No es una tarea trivial, pero las ganancias y el uso pueden ser mínimos.

+0

pero ¿no sería cierto el argumento sobre el tamaño de la base de datos incluso si OP tiene una tabla _HIST? – Learning

+0

@zendar, ¿Usas particionamiento? – tuinstoel

+0

No. Las cosas funcionan bien de la manera que son ahora. Es solo que tenemos 5 veces más registros de los que deberíamos tener. Lo hicimos porque era "una característica muy importante" que nadie necesita ahora. Así que cuestioné la motivación de Asaf para esto. Tal vez pueda ahorrar algo de tiempo y dolor. – zendar

0

Usaría la partición IS_LAST=1 y el sistema de partición IS_LAST=0. Debido a que está particionado, será rápido (eliminación de particiones) y nunca tendrá que consultar una unión de la tabla normal y la tabla de historial.

Yo usaría IS_LAST = 'Y'/'N' y no 1/0. 1 y 0 no tienen sentido.

Hay un truco especial que puede ayudar a guarrantee que hay un máximo de una fila con IS_LAST='Y' por entidad: Se puede crear un índice basado en la función única con una función que devuelve un valor nulo cuando IS_LAST='N' y devolver el ID cuando IS_LAST='Y'.Se explica aquí: http://www.akadia.com/services/ora_function_based_index_1.html

1

Si tengo 1 o 2 tablas de historia para conservar, lo haría exactamente como sugirió Tuinstoel. Pero si tuviera docenas de tablas para hacer esto, me movería más hacia una solución descrita por zendar. El motivo es esto

¿Cómo responder a preguntas como,

  • lo que ha cambiado desde ayer, cuando todo estaba bien?

  • ¿El usuario SMITHG ha realizado algún cambio?

Esas preguntas requieren una consulta por tabla, ya sea una tabla _hist separada o una partición dentro de la tabla. No importa, es una gran lista de consultas. Si tienes una mesa central que se ve así, entonces es un pedazo de pastel.

table_name, Column_name, PK, Before_value, After_value, User, timestamp 

inserciones tener sólo después de valores,

Elimina tener sólo ante los valores,

actualización tener ambos, pero sólo para las columnas que han cambiado.

Algunas variaciones

Puede incluir una columna para E/T/D si lo prefiere Puede excluir valores de las columnas de los aislantes y simplemente grabar el PK y desde los valores correctos se encuentran todavía en la mesa.

Dado que este es Oracle, puede dividir en table_name, por lo que, en esencia, usted realmente tiene una hist "tabla" por tabla real.

Puede responder fácilmente a las preguntas anteriores, que creo que son, simplemente, las preguntas más frecuentes. Y maneja todas las preguntas que puede responder con particiones o tablas _hist.

0

Realizar un seguimiento en función del tiempo lo ayudará a lograr el efecto que busca a diario y al final del negocio o a media noche, dependiendo del tiempo de volumen de transacción más bajo si ejecutó un procedimiento para mover datos al final en la tabla de historia, ¿sería útil? De esa forma, todas sus actualizaciones serían insertables y no se requiere bloqueo también. Saludos, Andy

1

Dado que está utilizando Oracle, puede marcar Oracle Flashback Technology. Registra cambios de todos los cambios en la base de datos, tanto datos como estructura. También registra la marca de tiempo y el nombre de usuario.

No lo usé, pero parece capaz.

+0

Esta es solo una característica de llg, si están en eso, entonces genial, de lo contrario, no es una opción. –

+0

Flashback se presenta en 9i, hace unos 8-9 años. En la versión 10g, se volvió bastante maduro. – zendar

0

Todo depende de lo que tiene:

  • ¿Está ejecutando Standard o Enterprise Edition? El particionamiento solo se incluye como una opción en la parte superior de Enterprise Edition. Más información sobre eso here.
  • Puede considerar ir con Workspace Manager para hacerlo si está buscando una solución fácil donde no tenga que mantener su propio código. Sin embargo, hay algunas limitaciones que he encontrado (por ejemplo, el mantenimiento del índice Oracle Text parece ser difícil, si no imposible, aunque solo lo he visto en 10gR2).
  • De lo contrario, me gustaría ir con la solución de zvolkov (tabla activa con un disparador escribiendo en la tabla de historial) o la solución de Mark Brady (registro de cambios). He usado ambos patrones y cada uno tiene sus pros y sus contras.
  • @zendar: la consulta de Flashback solo funciona desde la última vez que se deshace. No es una solución a largo plazo, solo una solución para mirar atrás unas pocas horas (dependiendo de la retención de deshacer que haya especificado).
0

Al igual que en otros, utilizo un ORM (Propel) con un objeto base que contiene métodos de eliminación guardados personalizados &. Estos métodos anulan el estándar guardar & eliminar que viene con el ORM. Comprueban para ver qué columnas han cambiado y crean 1 fila en la tabla de cambios para cada columna modificada.

Esquema para change tabla: change_pk, user_fk, nombre_usuario, session_id, dirección_ip, método, nombre_tabla, row_fk, nombre_campo, FIELD_VALUE, most_recent, fecha_hora

Ejemplo: 1, 4232, 'Gnarls Barkley', ' f2ff3f8822ff23 ',' 234.432.324.694 ',' ACTUALIZACIÓN ',' Usuario ', 4232,' primer nombre ',' Gnarles ',' S ',' 2009-08-20 10:10:10 ';

Cuestiones relacionadas