Conceitualmente falando, embora em seu ambiente de negócios, Ordem e Endereço sejam idéias estreitamente associadas, elas são, na verdade, dois tipos de entidade separados, cada um com seu próprio conjunto de propriedades (ou atributos) e restrições aplicáveis.
Portanto, como declarado anteriormente nos comentários, concordo com o @Erik , e você deve organizar o layout lógico do seu banco de dados declarando entre outros elementos:
- uma tabela discreta para manter as informações do endereço ;
- uma tabela para reter detalhes específicos do cliente ;
- uma tabela para incluir pontos de dados do pedido ; e
- uma tabela para conter fatos sobre as associações entre Cliente (s) e Endereço (s) ;
como vou exemplificar abaixo.
Diagrama do IDEF1X expositivo
Uma imagem vale mais que mil palavras, por isso criei o diagrama IDEF1X mostrado na Figura 1 para ilustrar algumas das possibilidades abertas pela minha sugestão:
Cliente , endereço e suas associações
Como demonstrado, eu retratada uma associação com um de muitos para muitos (M: N) cardinalidade proporção entre os tipos de entidade de cliente um e de endereços ; essa abordagem forneceria flexibilidade futura porque, como você sabe, um cliente pode manter vários endereços ao longo do tempo, ou mesmo simultaneamente, e o mesmo endereço pode ser compartilhado por vários clientes .
Um endereço específico pode ser usado de várias maneiras por clientes um para muitos (1: M) ; por exemplo, pode ser definido como físico e / ou definido para remessa e / ou cobrança . Talvez, a mesma instância de Endereço possa atender a cada um dos propósitos mencionados ao mesmo tempo, ou pode estar cobrindo dois usos, enquanto uma ocorrência de Endereço diferente cobre o restante.
a Em alguns ambientes de negócios, um Cliente pode ser uma Pessoa ou uma Organização (situação que implicaria um arranjo ligeiramente distinto, conforme detalhado nesta resposta sobre uma estrutura de supertipo-subtipo), mas com o objetivo de fornecer um exemplo simplificado, decidi para não incluir essa possibilidade aqui. Caso você precise cobrir essa situação em seu banco de dados, a postagem do link anterior mostra o método para resolver o requisito.
Ordem , endereço , CustomerAddress e Roles endereço
Geralmente, um pedido requer apenas dois tipos de endereços , um para remessa e outro para cobrança . Dessa maneira, a mesma instância de Endereço pode preencher as duas Funções de um Pedido individual , mas cada Função é representada pela propriedade respectiva, ou seja, ShippingAddressId ou BillingAddressId .
O pedido é conectado ao Endereço por meio do tipo de entidade associativa CustomerAddress com o auxílio de duas CHAVES ESTRANGEIRAS de várias propriedades, ou seja,
- ( CustomerNumber , ShippingAddressId ) e ( CustomerNumber , BillingAddressId ),
ambos apontando para a CHAVE PRIMÁRIA de várias propriedades CustomerAddress mostrada como
- ( CustomerNumber , AddressId )
... o que ajuda a representar uma regra comercial que estipula que (a) uma instância do Pedido deve ser vinculada exclusivamente a (b) Ocorrências de endereços anteriormente associadas ao Cliente específico que fez esse Pedido e nunca com (c) um não Cliente aleatório - Endereço relacionado .
Histórico para (1) Endereço e (2) para a associação CustomerAddress
Se você deseja fornecer a possibilidade de modificar as informações do Endereço , deve acompanhar todas as alterações de dados. Dessa maneira, descrevi Address como um tipo de entidade "auditável" que mantém seu próprio AddressHistory .
Como a natureza de uma conexão entre um Cliente e um Endereço também pode sofrer uma ou mais modificações, também descrevi a possibilidade de lidar com essa associação como uma associação “auditável” em virtude do tipo de entidade CustomerAddressHistory .
A esse respeito, vários fatores abordados nas perguntas e respostas não. 1 e perguntas e respostas não. 2 , tanto sobre ativar recursos temporais em um banco de dados, são realmente relevantes.
Layout lógico SQL-DDL ilustrativo
Consequentemente, em termos do diagrama exibido e explicado acima, declarei a seguinte organização de nível lógico (que você pode adaptar para atender às suas necessidades com exatidão):
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also, you should make accurate tests to define the
-- most convenient INDEX strategies based on the exact
-- data manipulation tendencies of your business domain.
-- As one would expect, you are free to utilize
-- your preferred (or required) naming conventions.
CREATE TABLE Customer (
CustomerNumber INT NOT NULL,
SpecificAttribute CHAR(30) NOT NULL,
ParticularAttribute CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Customer_PK PRIMARY KEY (CustomerNumber)
);
CREATE TABLE Address (
AddressId INT NOT NULL,
SpecificAttribute CHAR(30) NOT NULL,
ParticularAttribute CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Address_PK PRIMARY KEY (AddressId)
);
CREATE TABLE CustomerAddress (
CustomerNumber INT NOT NULL,
AddressId INT NOT NULL,
IsPhysical BIT NOT NULL,
IsShipping BIT NOT NULL,
IsBilling BIT NOT NULL,
IsActive BIT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT CustomerAddress_PK PRIMARY KEY (CustomerNumber, AddressId),
CONSTRAINT CustomerAddressToCustomer_FK FOREIGN KEY (CustomerNumber)
REFERENCES Customer (CustomerNumber),
CONSTRAINT CustomerAddressToAddress_FK FOREIGN KEY (AddressId)
REFERENCES Address (AddressId)
);
CREATE TABLE MyOrder (
CustomerNumber INT NOT NULL,
OrderNumber INT NOT NULL,
ShippingAddressId INT NOT NULL,
BillingAddressId INT NOT NULL,
SpecificAttribute CHAR(30) NOT NULL,
ParticularAttribute CHAR(30) NOT NULL,
OrderDate DATE NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Order_PK PRIMARY KEY (CustomerNumber, OrderNumber),
CONSTRAINT OrderToCustomer_FK FOREIGN KEY (CustomerNumber)
REFERENCES Customer (CustomerNumber),
CONSTRAINT OrderToShippingAddress_FK FOREIGN KEY (CustomerNumber, ShippingAddressId)
REFERENCES CustomerAddress (CustomerNumber, AddressId),
CONSTRAINT OrderToBillingAddress_FK FOREIGN KEY (CustomerNumber, BillingAddressId)
REFERENCES CustomerAddress (CustomerNumber, AddressId)
);
CREATE TABLE AddressHistory (
AddressId INT NOT NULL,
AuditedDateTime DATETIME NOT NULL,
SpecificAttribute CHAR(30) NOT NULL,
ParticularAttribute CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT AddressHistory_PK PRIMARY KEY (AddressId, AuditedDateTime),
CONSTRAINT AddressHistoryToAddress_FK FOREIGN KEY (AddressId)
REFERENCES Address (AddressId)
);
CREATE TABLE CustomerAddressHistory (
CustomerNumber INT NOT NULL,
AddressId INT NOT NULL,
AuditedDateTime DATETIME NOT NULL,
IsPhysical BIT NOT NULL,
IsShipping BIT NOT NULL,
IsBilling BIT NOT NULL,
IsActive BIT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT CustomerAddressHistory_PK PRIMARY KEY (CustomerNumber, AddressId, AuditedDateTime),
CONSTRAINT CustomerAddressHistoryToCustomerAddress_FK FOREIGN KEY (CustomerNumber, AddressId)
REFERENCES CustomerAddress (CustomerNumber, AddressId)
);
Se você quiser dar uma olhada, testei-o neste db <> violino que é executado no SQL Server 2017.
As History
mesas
O seguinte trecho da sua pergunta é muito importante:
O que estou procurando é como posso configurar meus endereços para que, quando os editar, o pedido não seja afetado pelo fato de um cliente atualizar seu endereço ou se mudar.
As tabelas AddressHistory
e CustomerAddressHistory
ajudam a garantir que um pedido não seja afetado pelas alterações de endereço , pois todas as linhas "anteriores" devem ser mantidas na respectiva History
tabela e podem ser consultadas quando necessário. As operações UPDATE e DELETE nessas duas tabelas devem ser proibidas (tentar alterar o histórico pode até ter implicações legais negativas).
O Intervalo englobou os valores incluídos AddressHistory.CreatedDateTime
e AddressHistory.AuditedDateTime
representa todo o Período durante o qual uma determinada Address
linha "passada" foi considerada "presente", "atual" ou "efetiva". Considerações semelhantes se aplicam às CustomerAddressHistory
linhas.
A CustomerAddress.IsActive
coluna BIT (booleana) deve indicar se uma determinada Address
linha é "utilizável" por uma Customer
linha ou não; por exemplo, se estiver definido como 'false', transmitiria o fato de que um cliente não está mais usando esse endereço e, portanto, não pode ser usado para novos pedidos .
Nota : Por outro lado, eu vi alguns sistemas nos quais, toda vez que um novo pedido é efetuado, as informações do endereço devem ser inseridas (algumas vezes repetidamente) e os endereços usados para pedidos anteriores nunca são apagados (portanto, os pedidos não são afetados pelas alterações de endereço ).
Esse curso de ação pode, decididamente, envolver volumes substanciais de redundância, mas é uma possibilidade - dependendo dos requisitos informacionais exatos do seu domínio de negócios - poderia funcionar, então você também pode avaliar seus prós e contras.
Recuperação de dados
A versão "presente", "atual" ou "efetiva" de uma ocorrência de Endereço deve estar contida como uma linha na Address
tabela, mas SELECIONAR os "estados" anteriores de um Endereço A partir da tabela AddressHistory
(ou da CustomerAddressHistory
) é fácil e pode um exercício interessante para aprimorar suas habilidades de codificação SQL.
Com relação a uma das situações mencionadas nos comentários, se você deseja recuperar a “penúltima versão” de uma Address
linha individual DA AddressHistory
, deve levar em consideração MAX(AddressHistory.AuditedDateTime)
oe o AddressHistory.AddressId
que corresponde ao Address.AddressId
valor específico em questão.
Nesse sentido, pelo menos ao criar um banco de dados relacional , é bastante conveniente definir primeiro o esquema conceitual correspondente (com base nas regras de negócios aplicáveis ) e depois declarar seu arranjo lógico de DDL subsequente . Depois de obter versões estáveis e confiáveis desses elementos fundamentais (que, é claro, podem evoluir com o tempo), é hora de analisar e determinar as melhores maneiras de manipular (via operações INSERT, UPDATE, DELETE e SELECT ou combinações delas) o sobre dados.
Percepção dos usuários finais, visões e assistência do (s) aplicativo (s)
Evidentemente, no nível externo da abstração, as informações de Endereço são percebidas (pelos usuários finais) como parte de um Pedido , e não há nada de errado nisso, mas isso não significa que os modeladores tenham que projetar as partes significativas do banco de dados em questão assim. Nesse ponto, se houver a necessidade de, por exemplo, imprimir um pedido "completo" (muito viável), você poderá "reproduzi-lo" sob demanda com a ajuda de alguns operadores JOIN e cláusulas WHERE (considerando o período de validade relacionado) , etc.) podem ser fixados em visualizações para consumo futuro, enviando o resultado pertinente definido para o (s) programa (s) aplicativo (s) relacionado (s) que, por sua vez, podem aprimorar sua formatação conforme necessário.
Obviamente, o (s) programa (s) aplicativo (s) também será muito útil quando uma Ordem estiver sendo efetivada; por exemplo, uma janela de aplicativo para computador / dispositivo móvel ou uma página da web pode:
- exibir apenas os endereços que o Cliente envolvido estabeleceu como “utilizável” (via
CustomerAddress.IsActive
);
- listar todos os endereços que o cliente ativou para o serviço de cobrança (via
CustomerAddress.IsBilling
); e
- agrupe todos os endereços que o cliente definiu para o serviço de remessa (via
CustomerAddress.IsShipping
);
facilitando dessa maneira todos os processos envolvidos na GUI (ou seja, o nível externo de abstração de um sistema computadorizado).
Leitura sugerida
Você solicitou (nos comentários agora removidos) algumas dicas sobre literatura de banco de dados de som; portanto, quanto ao material teórico , eu recomendo que você leia todo o trabalho escrito pelo Dr. EF Codd , um ganhador do Prêmio Turing e, é claro, o único criador do modelo relacional de dados (talvez agora mais relevante do que nunca). Esta lista inclui alguns de seus artigos e documentos tremendamente influentes.
Dois trabalhos importantes que não constam da lista mencionada são, precisamente, sua palestra do Prêmio ACM Turing, intitulada Banco de dados relacional: uma base prática para a produtividade , de 1981, e seu livro denominado Modelo relacional para gerenciamento de banco de dados: versão 2 , publicado em 1990.
Na frente do projeto conceitual , a Definição Integrada para Modelagem de Informações (IDEF1X) é uma técnica seriamente recomendável que foi definida como padrão em dezembro de 1993 pelo Instituto Nacional de Padrões e Tecnologia dos EUA (NIST).
MyOrder.ShippingAddressId
eMyOrder.BillingAddressId
devem fazer referência aCustomerAddress.AddressId
(e não aAddress.AddressId
); dessa maneira, assegura-se de que um Pedido possa ser associado exclusivamente aos endereços anteriormente conectados ao Cliente que fez esse Pedido . O diagrama sugere esse arranjo, para que o DDL seja mais preciso. Obrigado por solicitar esse esclarecimento.History
tabela, deveria ser o mesmo para aAddress
tabela? E se o Cliente pedir algo e depois alterar apenas o código postal ou cidade apenas um campo. Devemos inserir o endereço existenteHistory
e depois fazer uma nova inserção naAddress
tabela, certo?Address
linha correspondente que estava "presente" até que a modificação ocorreu seja INSERIDA naAddressHistory
tabela e também que (b ) aAddress
linha em questão é UPDATEd com os novos valores. Seria vantajoso executar esse processo como uma única unidade de trabalho dentro de uma transação.Esta resposta foi compilada dos comentários à pergunta.
Uma solução seria usar um FK para a tabela de endereços na tabela de pedidos. Isso permitirá que você veja os endereços que foram usados para o pedido e separa o endereço do endereço atual do usuário.
Para fazer isso funcionar, você teria que inserir um novo endereço e vincular esse novo endereço à tabela Usuário. Isso significa que os endereços são gravados uma vez e a edição é uma ilusão para o usuário final. Você pode armazenar efetivamente o histórico de todos os endereços aos quais um usuário foi associado movendo a associação da tabela Usuário para uma tabela de associação com um carimbo de data / hora. Isso forneceria um histórico de edições / endereços e manteria dados imutáveis na tabela Endereço.
@MDCCL declarou:
O MDCCL também forneceu uma visão geral sobre como encontrar o endereço atual de um usuário aqui:
fonte