Práticas recomendadas para tabelas de histórico / temporais?

11

Suponha que eu tenha um objeto, com certos campos que desejo rastrear o histórico e certos campos que não quero rastrear o histórico. De uma perspectiva de normalização, está correto o seguinte 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,
)

onde MyObjectHistory contém os campos rastreados para todos, exceto a revisão mais recente. Ou, todos os campos rastreados devem estar em uma tabela e todas as revisões, incluindo as mais recentes, devem estar nessa tabela, como em:

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
fonte
Concordo com
@Joel

Respostas:

7

Por motivos práticos de acesso a dados, você deve usar a estrutura da sua primeira opção, mas mantenha todas as versões dos valores das colunas rastreadas, incluindo a versão atual em sua tabela de histórico.

A razão para isso é que, em geral, quando você deseja examinar o histórico, deseja incluir a versão atual e todas as versões anteriores. Quando você não quer olhar para a história, você a quer fora do caminho. Em muitos casos, isso significa ir até o ponto de segregar o histórico em um esquema ou banco de dados separado. Mesmo se você mantiver seu histórico no mesmo esquema que os dados atuais, qualquer consulta que analise dados históricos (incluindo os valores atuais) será muito mais complexa, pois eles precisam unir basicamente duas fontes.

Joel Brown
fonte
2

Eu preferiria a primeira versão porque você provavelmente raramente precisará ver o histórico, mas frequentemente precisará ver o valor atual. Uma tabela de histórico deve ser preenchida a partir de um gatilho, para que você não precise se preocupar com a sincronização geral dos dados. Então, suponha que você tenha um milhão de registros no MyObject e, em seguida, 10.000.000 de registros no MyObjectHistory. Deseja realmente ingressar em uma tabela com tantos registros para obter o valor atual?

Agora, se você precisar consultar o histórico com frequência ou mais frequência que o valor atual, a segunda estrutura funcionará. (E se você estiver exibindo o valor em uma data específica, eu teria um campo de início e de término para facilitar a consulta.)

BTW, eu adicionaria um campo de data à tabela de histórico para poder dizer em que ordem as alterações ocorreram. Você não pode confiar em identidades para ordem temporal. Além disso, se houver uma dúvida sobre um valor anterior e quando ele for alterado, você precisará saber. Também posso colocar valores para o aplicativo de onde a alteração veio (se você tiver vários aplicativos) e / ou a pessoa que fez a alteração.

HLGEM
fonte
0

Existem algumas razões importantes para o número 1. A primeira é a questão do tamanho que HLGEM aponta, mas também existem outras importantes.

Normalmente, sua trilha de auditoria terá requisitos a serem desenvolvidos ao longo do tempo. Você pode querer rastrear os usuários do banco de dados, a hora da mudança etc. Os requisitos da trilha de auditoria e sua tabela principal provavelmente mudarão ao longo do tempo de maneira independente. Finalmente, é provável que você queira limpar os dados da trilha de auditoria após um período de tempo independente e em uma tabela totalmente separada.

É claro que pode haver casos em que você deseja mesclá-los completamente (como fazemos para as taxas de imposto no LedgerSMB) porque dados históricos podem ser usados ​​para cálculos atuais e o número de registros provavelmente é relativamente pequeno.

No entanto, vou sugerir que o armazenamento de objetos em tabelas como essa raramente leva a bons projetos normalizados. Na minha experiência, você realmente deseja algum encapsulamento entre um bom armazenamento normalizado e um modelo de objeto de aplicativo.

Chris Travers
fonte
2
O que você quer dizer com "encapsulamento entre um bom armazenamento normalizado e um modelo de objeto de aplicativo"? Você explicaria essa idéia ou daria um exemplo?
cubetwo1729