¿Mejores prácticas para tablas de historia / temporales?

11

Supongamos que tengo un objeto, con ciertos campos de los que quiero hacer un seguimiento del historial, y ciertos campos de los que no quiero hacer un seguimiento del historial. Desde una perspectiva de normalización, está bien el siguiente esquema:

CREATE TABLE MyObject AS (
    MyObjectId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectField1 VARCHAR(100) NOT NULL,
    MyObjectField2 VARCHAR(100) NOT NULL,
    MyObjectField3 VARCHAR(100) NOT NULL,
    MyObjectTrackedField1 VARCHAR(100) NOT NULL,
    MyObjectTrackedField2 VARCHAR(100) NOT NULL,
    MyObjectTrackedField3 VARCHAR(100) NOT NULL,
)
CREATE TABLE MyObjectHistory AS (
    MyObjectHistoryId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectId INT NOT NULL FOREIGN KEY REFERENCES MyObject(MyObjectId),
    MyObjectTrackedField1 VARCHAR(100) NOT NULL,
    MyObjectTrackedField2 VARCHAR(100) NOT NULL,
    MyObjectTrackedField3 VARCHAR(100) NOT NULL,
)

donde MyObjectHistory contiene los campos rastreados para todos excepto para la última revisión. O bien, todos los campos rastreados deben estar en una tabla, y todas las revisiones, incluida la última, deben estar en esa tabla, como en:

CREATE TABLE MyObject AS (
    MyObjectId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectField1 VARCHAR(100) NOT NULL,
    MyObjectField2 VARCHAR(100) NOT NULL,
    MyObjectField3 VARCHAR(100) NOT NULL,
)
CREATE TABLE MyObjectHistory AS (
    MyObjectHistoryId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectId INT NOT NULL FOREIGN KEY REFERENCES MyObject(MyObjectId),
    MyObjectTrackedField1 VARCHAR(100) NOT NULL,
    MyObjectTrackedField2 VARCHAR(100) NOT NULL,
    MyObjectTrackedField3 VARCHAR(100) NOT NULL,
)
cubetwo1729
fuente
Estoy de acuerdo con
@Joel

Respuestas:

7

Por razones prácticas de acceso a datos, debe usar la estructura de su primera opción, pero en su lugar debe mantener todas las versiones de los valores de las columnas rastreadas, incluida la versión actual en su tabla de historial.

La razón de esto es que, en general, cuando desea ver el historial, desea incluir el presente y todas las versiones pasadas. Cuando no quieres mirar el historial, lo quieres fuera del camino. En muchos casos, esto significa ir tan lejos como para segregar el historial en un esquema o base de datos por separado. Incluso si mantiene su historial en el mismo esquema que sus datos actuales, cualquier consulta que analice los datos históricos (incluidos los valores actuales) será mucho más compleja ya que esencialmente tienen que unir dos fuentes.

Joel Brown
fuente
2

Preferiría la primera versión porque probablemente rara vez necesites ver el historial, pero con frecuencia necesitarás ver el valor actual. Una tabla de historial debe rellenarse a partir de un desencadenador, por lo que no debe preocuparse por la falta de sincronización de los datos en general. Supongamos que tiene un millón de registros en MyObject y luego tiene 10,000,000 registros en MyObjectHistory. ¿Realmente desea unirse a una tabla con tantos registros para obtener el valor actual?

Ahora, si necesita consultar el historial con tanta frecuencia o más frecuencia que el valor actual, entonces la segunda estructura funcionaría. (Y si va a mostrar el valor a partir de una fecha en particular, tendría un campo de fecha de inicio y una fecha de finalización para simplificar las consultas).

Por cierto, agregaría un campo de fecha a la tabla de historial para poder decir en qué orden ocurrieron los cambios. No puede confiar en las identidades para el orden temporal. Además, si hay una pregunta sobre un valor anterior y cuando se modificó, deberá saberlo. También podría poner valores para la aplicación de la que provino el cambio (si tiene varias aplicaciones) y / o la persona que realizó el cambio.

HLGEM
fuente
0

Hay un par de razones importantes para # 1. El primero es el problema de tamaño que señala HLGEM, pero también hay otros importantes.

Por lo general, su seguimiento de auditoría tendrá requisitos desarrollados con el tiempo. Es posible que desee realizar un seguimiento de los usuarios de la base de datos, el tiempo de cambio, etc. Es probable que los requisitos de seguimiento de auditoría y su tabla principal cambien con el tiempo de forma algo independiente. Finalmente, es probable que desee purgar los datos de seguimiento de auditoría después de un período de tiempo de forma independiente y una tabla completamente separada.

Por supuesto, puede haber casos en los que desee fusionarlos por completo (como lo hacemos para las tasas impositivas en LedgerSMB) porque los datos históricos pueden usarse para los cálculos actuales y es probable que el número de registros sea relativamente pequeño.

Sin embargo, voy a sugerir que almacenar objetos en tablas como esta rara vez conduce a diseños buenos y normalizados. En mi experiencia, realmente desea un poco de encapsulación entre un buen almacenamiento normalizado y un modelo de objeto de aplicación.

Chris Travers
fuente
2
¿Qué quiere decir con "encapsulación entre un buen almacenamiento normalizado y un modelo de objeto de aplicación"? ¿Podría exponer esta idea o dar un ejemplo?
cubetwo1729