Cómo almacenar datos históricos

162

Algunos compañeros de trabajo y yo entramos en un debate sobre la mejor manera de almacenar datos históricos. Actualmente, para algunos sistemas, uso una tabla separada para almacenar datos históricos y mantengo una tabla original para el registro activo actual. Entonces, digamos que tengo mesa FOO. Bajo mi sistema, todos los registros activos irán en FOO, y todos los registros históricos irán en FOO_Hist. El usuario puede actualizar muchos campos diferentes en FOO, por lo que quiero mantener una cuenta precisa de todo lo actualizado. FOO_Hist contiene exactamente los mismos campos que FOO con la excepción de un HIST_ID de incremento automático. Cada vez que se actualiza FOO, realizo una instrucción de inserción en FOO_Hist similar a: insert into FOO_HIST select * from FOO where id = @id.

Mi compañero de trabajo dice que este es un mal diseño porque no debería tener una copia exacta de una tabla por razones históricas y simplemente debería insertar otro registro en la tabla activa con un indicador que indique que es para fines históricos.

¿Existe un estándar para tratar con el almacenamiento de datos históricos? Me parece que no quiero saturar mis registros activos con todos mis registros históricos en la misma tabla, considerando que puede haber más de un millón de registros (estoy pensando a largo plazo).

¿Cómo usted o su empresa manejan esto?

Estoy usando MS SQL Server 2008, pero me gustaría mantener la respuesta genérica y arbitraria de cualquier DBMS.

Aaron
fuente

Respuestas:

80

Apoyar los datos históricos directamente dentro de un sistema operativo hará que su aplicación sea mucho más compleja de lo que sería de otra manera. En general, no recomendaría hacerlo a menos que tenga un requisito difícil de manipular versiones históricas de un registro dentro del sistema.

Si observa detenidamente, la mayoría de los requisitos para los datos históricos se dividen en una de dos categorías:

  • Registro de auditoría: es mejor hacerlo con tablas de auditoría. Es bastante fácil escribir una herramienta que genere scripts para crear tablas de registro de auditoría y disparadores leyendo metadatos del diccionario de datos del sistema. Este tipo de herramienta se puede utilizar para actualizar el registro de auditoría en la mayoría de los sistemas. También puede usar este subsistema para la captura de datos modificados si desea implementar un almacén de datos (ver más abajo).

  • Informes históricos : informes sobre el estado histórico, posiciones 'as-at' o informes analíticos a lo largo del tiempo. Es posible cumplir con requisitos de informes históricos simples al consultar las tablas de registro de auditoría del tipo descrito anteriormente. Si tiene requisitos más complejos, puede ser más económico implementar una despensa de datos para la presentación de informes que intentar integrar el historial directamente en el sistema operativo.

    Las dimensiones que cambian lentamente son, con mucho, el mecanismo más simple para rastrear y consultar el estado histórico y gran parte del seguimiento del historial se puede automatizar. Los controladores genéricos no son tan difíciles de escribir. En general, los informes históricos no tienen que usar datos actualizados, por lo que un mecanismo de actualización por lotes normalmente está bien. Esto mantiene la arquitectura de su núcleo y sistema de informes relativamente simple.

Si sus requisitos caen en una de estas dos categorías, probablemente sea mejor que no almacene datos históricos en su sistema operativo. La separación de la funcionalidad histórica en otro subsistema probablemente requerirá menos esfuerzo en general y producirá bases de datos transaccionales y de auditoría / informes que funcionarán mucho mejor para su propósito previsto.

Preocupado por TunbridgeWells
fuente
Creo que veo lo que estás diciendo. Entonces, lo que hice con mi tabla FOO_Hist fue realmente crear una tabla de auditoría. En lugar de usar un disparador para insertar en la tabla de auditoría en la actualización, acabo de ejecutar una declaración en el programa. ¿Es eso correcto?
Aaron
66
Más o menos. Sin embargo, es mejor hacer este tipo de registro de auditoría con disparadores; los disparadores aseguran que cualquier cambio (incluidas las correcciones de datos manuales) se registre en los registros de auditoría. Si tiene más de 10-20 tablas para auditar, probablemente sea más rápido construir una herramienta generadora de disparadores. Si el tráfico de disco para los registros de auditoría es un problema, puede colocar las tablas de registro de auditoría en un conjunto separado de discos.
Preocupado por TunbridgeWells
Sí, estoy 100% de acuerdo con eso. Gracias.
Aaron
40

No creo que haya una forma estándar particular de hacerlo, pero pensé que incluiría un posible método. Trabajo en Oracle y en nuestro marco interno de aplicaciones web que utiliza XML para almacenar datos de aplicaciones.

Usamos algo llamado Modelo Maestro - Detalle que, en su forma más simple, consiste en:

La tabla maestra, por ejemplo, a Widgetsmenudo se llama solo con un ID. A menudo contendrá datos que no cambiarán con el tiempo / no son históricos.

Tabla de detalles / historial, por ejemplo, llamada que Widget_Detailscontiene al menos:

  • ID: clave principal. Detalle / ID histórico
  • MASTER_ID: por ejemplo, en este caso llamado 'WIDGET_ID', este es el FK para el registro maestro
  • START_DATETIME: marca de tiempo que indica el inicio de esa fila de la base de datos
  • END_DATETIME: marca de tiempo que indica el final de esa fila de la base de datos
  • STATUS_CONTROL: la columna de un solo carácter indica el estado de la fila. 'C' indica que actual, NULL o 'A' serían históricos / archivados. Solo usamos esto porque no podemos indexar si END_DATETIME es NULL
  • CREATED_BY_WUA_ID: almacena el ID de la cuenta que causó la creación de la fila
  • XMLDATA: almacena los datos reales

Entonces, esencialmente, una entidad comienza teniendo 1 fila en el maestro y 1 fila en el detalle. El detalle que tiene una fecha de finalización NULL y STATUS_CONTROL de 'C'. Cuando se produce una actualización, la fila actual se actualiza para tener END_DATETIME de la hora actual y status_control se establece en NULL (o 'A' si se prefiere). Se crea una nueva fila en la tabla de detalles, aún vinculada al mismo maestro, con status_control 'C', la identificación de la persona que realiza la actualización y los nuevos datos almacenados en la columna XMLDATA.

Esta es la base de nuestro modelo histórico. La lógica de Crear / Actualizar se maneja en un paquete Oracle PL / SQL, por lo que simplemente le pasa a la función la ID actual, su ID de usuario y los nuevos datos XML e internamente realiza todas las actualizaciones / inserciones de filas para representar eso en el modelo histórico . Las horas de inicio y finalización indican cuándo está activa esa fila de la tabla.

El almacenamiento es barato, generalmente no BORRAMOS datos y preferimos mantener un seguimiento de auditoría. Esto nos permite ver cómo se veían nuestros datos en un momento dado. Al indexar status_control = 'C' o usar una Vista, el desorden no es exactamente un problema. Obviamente, sus consultas deben tener en cuenta que siempre debe usar la versión actual (NULL end_datetime y status_control = 'C') de un registro.

Chris Cameron-Mills
fuente
Hola Chris, si haces eso, la ID (clave principal) debe cambiarse, ¿verdad? ¿Qué tal la relación con otra tabla si es utilizada por otra?
Projo
@projo el ID en su tabla maestra es el PK y conceptualmente el "PK" para cualquier concepto con el que esté tratando. El ID en la tabla de detalles es el PK para identificar una versión histórica para el maestro (que es otra columna sobre el detalle). Al formar relaciones, a menudo hace referencia a la verdadera PK de su concepto (es decir, la ID en su tabla maestra o la columna MASTER_ID en su detalle) y usa STATUS_CONTROL = 'C' para asegurarse de que está obteniendo la versión actual. Alternativamente, puede hacer referencia a la ID de detalle para relacionar algo con un punto particular en el tiempo.
Chris Cameron-Mills
+1 He implementado este patrón con gran éxito en varios proyectos grandes.
Three Value Logic
Estamos utilizando el mismo método, pero ahora me pregunto si es mejor almacenar solo START_DATETIME y no almacenar END_DATETIME
bat_ventzi
Un par de variaciones en mi experiencia. Si su entidad está "finalizada", es decir, archivada o eliminada, entonces podría no tener registros detallados con control de estado 'C', es decir, no hay fila actual, aunque no sabría cuándo sucedió eso. Alternativamente, puede establecer un end_datetime en la fila final y la presencia de una fila 'terminada' 'C' podría indicar que la entidad ahora se elimina / archiva. Finalmente, podría representar esto a través de otra columna, ESTADO que probablemente ya tenga.
Chris Cameron-Mills
15

Creo que tu enfoque es correcto. La tabla histórica debe ser una copia de la tabla principal sin índices, asegúrese de tener también la marca de tiempo de actualización en la tabla.

Si prueba el otro enfoque lo suficientemente pronto, enfrentará problemas:

  • gastos generales de mantenimiento
  • más banderas en selectos
  • consultas de ralentización
  • crecimiento de tablas, índices
Alejandro
fuente
7

En SQL Server 2016 y versiones posteriores , hay una nueva característica llamada Tablas temporales que tiene como objetivo resolver este desafío con el mínimo esfuerzo del desarrollador . El concepto de tabla temporal es similar a la Captura de datos de cambio (CDC), con la diferencia de que la tabla temporal ha abstraído la mayoría de las cosas que tenía que hacer manualmente si estaba usando CDC.

Benjamín
fuente
1

Solo quería agregar una opción que comencé a usar porque uso Azure SQL y lo de varias tablas fue demasiado engorroso para mí. Agregué un disparador de inserción / actualización / eliminación en mi tabla y luego convertí el cambio antes / después a json usando la función "FOR JSON AUTO".

 SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO)
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO)

Eso devuelve una representación JSON para el registro antes / después del cambio. Luego almaceno esos valores en una tabla de historial con una marca de tiempo de cuándo ocurrió el cambio (también almaceno el ID para el registro actual de preocupación). Utilizando el proceso de serialización, puedo controlar cómo se rellenan los datos en caso de cambios en el esquema.

Aprendí sobre esto en este enlace aquí

JakeHova
fuente
0

¿Podrías dividir las tablas no?

"Estrategias de tabla e índice particionadas con SQL Server 2008 Cuando una tabla de base de datos crece a cientos de gigabytes o más, puede ser más difícil cargar nuevos datos, eliminar datos antiguos y mantener índices. Solo el tamaño de la tabla hace que tales operaciones demoren mucho más. Incluso los datos que deben cargarse o eliminarse pueden ser muy considerables, lo que hace que las operaciones INSERT y DELETE en la tabla no sean prácticas. El software de base de datos Microsoft SQL Server 2008 proporciona particiones de tabla para hacer que tales operaciones sean más manejables ".

clyc
fuente
Sí, puedo particionar las tablas, pero ¿es ese el estándar cuando se trata de datos históricos? ¿Deberían incluirse los datos históricos en la misma tabla que los datos activos? Estas son las preguntas que quería discutir. Esto tampoco es arbitrario en lo que respecta a SQL Server 2008.
Aaron
0

La verdadera pregunta es ¿necesita usar datos históricos y datos activos juntos para informar? Si es así, manténgalos en una tabla, particione y cree una vista para registros activos para usar en consultas activas. Si solo necesita verlos ocasionalmente (para investigar problemas legales o algo así), póngalos en una tabla separada.

HLGEM
fuente
2
¿Es más difícil JOINdos tablas en un par de informes históricos o es más difícil modificar cada inserción / actualización / eliminación de cada tabla para tener en cuenta las preocupaciones históricas? En realidad, un registro de auditoría incluiría incluso los datos actuales en la tabla del historial, por lo que la tabla actual ni siquiera debería ser necesaria en un informe.
0

Otra opción es archivar los datos operativos sobre una base [diaria | por hora | lo que sea]. La mayoría de los motores de bases de datos admiten la extracción de datos en un archivo .

Básicamente, la idea es crear un trabajo programado de Windows o CRON que

  1. determina las tablas actuales en la base de datos operativa
  2. selecciona todos los datos de cada tabla en un archivo CSV o XML
  3. comprime los datos exportados a un archivo ZIP, preferiblemente con la marca de tiempo de la generación en el nombre del archivo para facilitar el archivo.

Muchos motores de bases de datos SQL vienen con una herramienta que puede usarse para este propósito. Por ejemplo, cuando se usa MySQL en Linux, el siguiente comando se puede usar en un trabajo CRON para programar la extracción:

mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz
Miguel
fuente
2
Esto no es del todo apropiado para los datos históricos porque si alguien cambia un valor y lo vuelve a cambiar dentro del ciclo de archivo, esas actualizaciones se pierden. Tampoco hay una manera fácil de ver los cambios en una entidad a lo largo del tiempo o restaurar una entidad parcialmente.
Sgoettschkes
0

Conozco esta publicación anterior, pero solo quería agregar algunos puntos. El estándar para tales problemas es lo que funciona mejor para la situación. Comprender la necesidad de dicho almacenamiento, y el uso potencial de los datos históricos / auditoría / seguimiento de cambios es muy importante.

Auditoría (propósito de seguridad) : use una tabla común para todas sus tablas auditables. defina la estructura para almacenar el nombre de la columna, antes del valor y después de los campos de valor.

Archivo / Histórico : para casos como el seguimiento de la dirección anterior, número de teléfono, etc., crear una tabla separada FOO_HIST es mejor si su esquema de tabla de transacción activa no cambia significativamente en el futuro (si su tabla de historial tiene que tener la misma estructura). si anticipa la normalización de la tabla, la adición / eliminación de columnas de cambio de tipo de datos, almacene sus datos históricos en formato xml. defina una tabla con las siguientes columnas (ID, Fecha, Versión del esquema, XMLData). esto manejará fácilmente los cambios de esquema. pero tiene que lidiar con xml y eso podría introducir un nivel de complicación para la recuperación de datos.

Danny D
fuente