Eu precisaria acompanhar as alterações de preço de um produto para poder consultar o banco de dados em busca de um preço de produto em uma determinada data. As informações são usadas em um sistema que calcula auditorias históricas, portanto, é necessário retornar o preço correto para o produto correto com base na data da compra.
Eu preferiria usar o postgres na construção do banco de dados.
Eu preciso do design do banco de dados, mas todas e quaisquer sugestões de boas práticas também são bem-vindas.
database-design
best-practices
Gunnar Norred
fonte
fonte
prices
crie uma tabelaprices_history
com colunas semelhantes. Hibernate Envers pode automatizar isso para vocêRespostas:
Se eu entendo o cenário adequadamente, você deve definir uma tabela que retenha uma série temporal de preço ; portanto, eu concordo, isso tem muito a ver com o aspecto temporal do banco de dados com o qual você está trabalhando.
Regras do negócio
Vamos começar a analisar a situação do nível conceitual. Então, se , no seu domínio comercial,
então isso significa que
O diagrama IDEF1X mostrado na Figura 1 , embora altamente simplificado, descreve um cenário como esse:
Layout lógico expositivo
E o design de nível lógico SQL-DDL a seguir, com base no referido diagrama IDEF1X, ilustra uma abordagem viável que você pode adaptar às suas próprias necessidades exatas:
A
Price
tabela possui uma CHAVE PRIMÁRIA composta de duas colunas, ou seja,ProductNumber
(restrita, por sua vez, como CHAVE ESTRANGEIRA que faz referência aProduct.ProductNumber
) eStartDate
(apontando a Data específica em que um determinado Produto foi comprado a um Preço específico ) .Caso os produtos sejam comprados a preços diferentes durante o mesmo dia , em vez da
StartDate
coluna, você pode incluir um rotulado paraStartDateTime
manter o instante em que um determinado produto foi comprado a um preço exato . A CHAVE PRIMÁRIA teria que ser declarada como(ProductNumber, StartDateTime)
.Como demonstrado, a tabela mencionada acima é comum, porque você pode declarar operações SELECT, INSERT, UPDATE e DELETE para manipular seus dados diretamente, portanto, (a) permite evitar a instalação de componentes adicionais e (b) pode ser usado em todos os as principais plataformas SQL com alguns poucos ajustes, se necessário.
Amostras de manipulação de dados
Para exemplificar algumas operações de manipulação que parecem úteis, digamos que você inseriu os seguintes dados nas tabelas
Product
ePrice
, respectivamente:Como o
Price.EndDate
é um ponto de dados derivável, você deve obtê-lo por meio de, precisamente, uma tabela derivada que pode ser criada como uma exibição para produzir a série temporal “completa”, como exemplificado abaixo:Em seguida, a operação a seguir que SELECT diretamente diretamente dessa exibição
fornece o próximo conjunto de resultados:
Agora, suponhamos que você esteja interessado em obter todos os
Price
dados dosProduct
principais identificados atéProductNumber
1750 emDate
2 de junho de 2017 . Vendo que umaPrice
asserção (ou linha) é atual ou efetiva durante todo o intervalo que vai de (i)StartDate
a (ii) suaEndDate
, então esta operação DMLproduz o conjunto de resultados a seguir
que aborda o referido requisito.
Como mostrado, a
PriceWithEndDate
exibição desempenha um papel primordial na obtenção da maioria dos dados deriváveis e pode ser SELECIONADA DE de uma maneira bastante comum.Considerando que sua plataforma preferida é o PostgreSQL, este conteúdo do site oficial de documentação contém informações sobre visualizações "materializadas" , que podem ajudar a otimizar a velocidade de execução por meio de mecanismos de nível físico, se esse aspecto se tornar problemático. Outros sistemas de gerenciamento de banco de dados SQL (DBMSs) oferecem instrumentos físicos muito parecidos, embora diferentes terminologias possam ser aplicadas, por exemplo, visualizações "indexadas" no Microsoft SQL Server.
Você pode ver as amostras de código DDL e DML discutidas em ação neste db <> fiddle e neste SQL Fiddle .
Recursos relacionados
Nestas perguntas e respostas , discutimos um contexto de negócios que inclui as alterações nos preços dos produtos, mas que tem um escopo mais extenso, para que você possa achar interessante.
Essas postagens de estouro de pilha cobrem pontos muito relevantes em relação ao tipo de uma coluna que contém um dado de moeda no PostgreSQL.
Respostas aos comentários
O método que proponho acima aborda um domínio comercial com as características descritas anteriormente , consequentemente aplicando sua sugestão sobre declarar o
EndDate
como uma coluna (que é diferente de um "campo") da tabela base nomeadaPrice
implicaria que a estrutura lógica do banco de dados seria não esteja refletindo o esquema conceitual corretamente, e um esquema conceitual deve ser definido e refletido com precisão, incluindo a diferenciação de (1) informações básicas de (2) informações deriváveis .Além disso, esse curso de ação introduziria duplicação, uma vez que a mesma
EndDate
poderia ser obtida em virtude de (a) uma tabela derivável e também em virtude de (b) a tabela base denominadaPrice
, com aEndDate
coluna duplicada . Embora essa seja uma possibilidade, se um profissional decidir seguir a abordagem, ele ou ela deve avisar decididamente os usuários do banco de dados sobre os inconvenientes e ineficiências que isso envolve. Um desses inconvenientes e ineficiências é, por exemplo, a necessidade urgente de desenvolver um mecanismo que garanta, a todo momento , que cadaPrice.EndDate
valor seja igual ao daPrice.StartDate
coluna da linha imediatamente sucessiva para oPrice.ProductNumber
valor em questão.Por outro lado, o trabalho para produzir os dados derivados em questão, como apresento, não é, honestamente, nada especial e é necessário para (i) garantir a correspondência correta entre os níveis lógico e conceitual de abstração do banco de dados e (ii) ) garantem a integridade dos dados, ambos os aspectos que foram observados anteriormente são decididamente de grande importância.
Se o aspecto de eficiência que você está falando estiver relacionado à velocidade de execução de algumas operações de manipulação de dados, ele deverá ser gerenciado no local apropriado, ou seja, no nível físico, por meio de, por exemplo, uma estratégia de indexação vantajosa, baseada em (1 ) as tendências de consulta específicas e (2) os mecanismos físicos específicos fornecidos pelo DBMS de uso. Caso contrário, sacrificar o mapeamento conceitual-lógico apropriado e comprometer a integridade dos dados envolvidos transforma facilmente um sistema robusto (ou seja, um ativo organizacional valioso) em um recurso não confiável.
Séries temporais descontínuas ou disjuntas
Por outro lado, há circunstâncias em que a retenção
EndDate
de cada linha em uma tabela de séries temporais não é apenas mais cômoda e eficiente, mas exigida , embora isso dependa inteiramente dos requisitos específicos do ambiente de negócios. Um exemplo desse tipo de circunstância ocorre quandoEu representei o referido cenário no diagrama IDEF1X exibido na Figura 2 .
Nesse caso, sim, a
Price
tabela hipotética deve ser declarada de maneira semelhante a esta:E, sim, esse design lógico de DDL simplifica a administração no nível físico, porque você pode criar uma estratégia de indexação que inclua a
EndDate
coluna (que, como mostrado, é declarada em uma tabela base) em configurações relativamente mais fáceis .Em seguida, uma operação SELECT como a abaixo
pode ser usado para derivar todos os
Price
dados para osProduct
primariamente identificados atéProductNumber
1750 emDate
2 de junho de 2017 .fonte
Eu acredito que você vai querer olhar para tabelas temporais . Eles fornecem funcionalidade para fazer exatamente o que você está procurando e estão disponíveis no Postgres com as extensões apropriadas.
Esse conceito também parece bastante independente de banco de dados, pois é oferecido em uma variedade de plataformas RDBMS .
fonte
Dei uma resposta aqui que é relativamente simples e não requer extensões especiais para o banco de dados (portanto, funcionará com qualquer banco de dados).
fonte