Tenho certeza de que muitos aplicativos, aplicativos críticos, bancos e outros fazem isso diariamente.
A ideia por trás de tudo o que é:
- todas as linhas devem ter um histórico
- todos os links devem permanecer coerentes
- deve ser fácil fazer solicitações para obter colunas "atuais"
- os clientes que compraram itens obsoletos ainda devem ver o que compraram, mesmo que este produto não faça mais parte do catálogo
e assim por diante.
Aqui está o que eu quero fazer e vou explicar os problemas que estou enfrentando.
Todas as minhas tabelas terão essas colunas:
id
id_origin
date of creation
start date of validity
start end of validity
E aqui estão as idéias para operações CRUD:
- create = insere nova linha com
id_origin
=id
,date of creation
= agora,start date of validity
= agora,end date of validity
= nulo (= significa que é o registro ativo atual) - update =
- read = lê todos os registros com
end date of validity
== null - atualize o registro "atual"
end date of validity
= null comend date of validity
= now - crie um novo com os novos valores e
end date of validity
= null (= significa que é o registro ativo atual)
- read = lê todos os registros com
- delete = atualize o registro "atual"
end date of validity
= null comend date of validity
= now
Então, aqui está o meu problema: com associações muitos-para-muitos. Vamos dar um exemplo com valores:
- Tabela A (id = 1, id_origin = 1, start = now, end = null)
- Tabela A_B (início = agora, final = nulo, id_A = 1, id_B = 48)
- Tabela B (id = 48, id_origin = 48, start = now, end = null)
Agora eu quero atualizar a tabela A, ID do registro = 1
- Marque a identificação do registro = 1 com final = agora
Eu insiro um novo valor na tabela A e ... caramba, perdi minha relação A_B, a menos que eu duplique a relação também ... isso terminaria em uma tabela:
Tabela A (id = 1, id_origin = 1, start = now, end = now + 8mn)
- Tabela A (id = 2, id_origin = 1, start = now + 8mn, end = null)
- Tabela A_B (início = agora, final = nulo, id_A = 1, id_B = 48)
- Tabela A_B (início = agora, final = nulo, id_A = 2, id_B = 48)
- Tabela B (id = 48, id_origin = 48, start = now, end = null)
E ... bem, eu tenho outro problema: a relação A_B: devo marcar (id_A = 1, id_B = 48) como obsoleta ou não (A - id = 1 é obsoleta, mas não B - 48)?
Como lidar com isso?
Eu tenho que projetar isso em grande escala: produtos, parceiros e assim por diante.
Qual a sua experiência nisso? Como você faria (como você fez)?
- Editar
Encontrei este artigo muito interessante , mas ele não lida adequadamente com "obsolescência de casos" (= o que estou perguntando realmente)
fonte
Respostas:
Não está claro para mim se esses requisitos são para fins de auditoria ou apenas uma referência histórica simples, como CRM e carrinhos de compras.
De qualquer maneira, considere ter uma tabela main e main_archive para cada área principal em que isso é necessário. "Main" terá apenas entradas atuais / ativas, enquanto "main_archive" terá uma cópia de tudo o que entra em main. Inserir / atualizar em main_archive pode ser um gatilho de inserir / atualizar em main. As exclusões no arquivo main_archive podem ser executadas por um longo período de tempo, se houver.
Para problemas referenciais, como o Cust X comprou o Produto Y, a maneira mais fácil de resolver sua preocupação referencial de cust_archive -> product_archive é nunca excluir as entradas do product_archive. Geralmente, a rotatividade deve ser muito menor nessa tabela, para que o tamanho não seja uma preocupação muito ruim.
HTH.
fonte
LP_
, e todas as tabelas importantes têm um equivalenteLH_
, com gatilhos inserindo linhas históricas na inserção, atualização e exclusão. Não funciona para todos os casos, mas tem sido um modelo sólido para as coisas que faço.UNION
exibição, o que permite fazer coisas legais, como impor uma restrição única nos registros atuais e históricos.id
" e "id_ref
".id_ref
é uma referência à idéia real da tabela. Exemplo:person
eperson_h
. emperson_h
eu tenho "id
" e "id_ref
" ondeid_ref
está relacionado a 'person.id
' para que eu possa ter muitas linhas com a mesmaperson.id
(= quando uma linha deperson
é modificada) e todasid
as minhas tabelas são autoinc.Isso tem alguma sobreposição com a programação funcional; especificamente o conceito de imutabilidade.
Você tem uma tabela chamada
PRODUCT
e outra chamadaPRODUCTVERSION
ou similar. Quando você altera um produto, não faz uma atualização, basta inserir uma novaPRODUCTVERSION
linha. Para obter as informações mais recentes, você pode indexar a tabela pelo número da versão (desc), carimbo de data / hora (desc) ou ter um sinalizador (LatestVersion
).Agora, se você tem algo que faz referência a um produto, pode decidir para qual tabela ele aponta. Aponta para a
PRODUCT
entidade (sempre refere-se a este produto) ou para aPRODUCTVERSION
entidade (refere-se apenas a esta versão do produto)?Isso fica complicado. E se você tiver fotos do produto? Eles precisam apontar para a tabela de versões, porque eles podem ser alterados, mas em muitos casos, eles não serão e você não deseja duplicar dados desnecessariamente. Isso significa que você precisa de uma
PICTURE
tabela e um relacionamentoPRODUCTVERSIONPICTURE
muitos-para-muitos.fonte
Eu implementei todas as coisas daqui com 4 campos que estão em todas as minhas tabelas:
Cada vez que um registro precisa ser modificado, eu o duplico, marque o registro duplicado como "antigo" =
date_validity_end=NOW()
e o atual como o bomdate_validity_start=NOW()
edate_validity_end=NULL
.O truque é sobre as relações de muitos para muitos e de um para muitos: funciona sem tocá-los! É tudo sobre as consultas que são mais complexas: para consultar um registro em uma data precisa (= agora não), tenho para cada associação e para a tabela principal adicionar essas restrições:
Assim, com produtos e atributos (muitos para muitos):
fonte
Que tal agora? Parece simples e bastante eficaz para o que fiz no passado. Na tabela "histórico", use um PK diferente. Portanto, o seu campo "CustomerID" é o PK na tabela Customer, mas na tabela "history", seu PK é "NewCustomerID". "CustomerID" se torna apenas outro campo somente leitura. Isso deixa "CustomerID" inalterado no Histórico e todos os seus relacionamentos permanecem intactos.
fonte