Quelqu'un peut-il m'aider à comprendre la réponse de cet utilisateur pour une table CustomerLocation . Je veux vraiment une bonne méthode pour stocker des adresses dans la table des commandes.
Ce que je recherche, c'est comment configurer mes adresses, donc lorsque je les modifie, la commande n'est pas affectée par le fait qu'un client met à jour son adresse ou déménage.
En l'état, mon schéma ressemble à:
Person |EntityID|
EntityAddress |EntityID|AddressID|
Address |AddressID|AddressType|AddressLine1|AddressLine2|
Order |OrderID|BillingAddressID|
Sur le plan conceptuel, bien que dans votre environnement professionnel Order et Address sont des idées étroitement associées, il s'agit en fait de deux types d'entités distincts , chacun avec son propre ensemble de propriétés (ou attributs) et de contraintes applicables.
Par conséquent, comme indiqué précédemment dans les commentaires, je suis d'accord avec @ Erik , et vous devez organiser la disposition logique de votre base de données en déclarant entre autres éléments:
comme je vais illustrer ci-dessous.
Une image vaut mille mots, j'ai donc créé le diagramme IDEF1X montré dans Figure 1 pour illustrer certaines des possibilités ouvertes par ma suggestion:
Client , Adresse et leurs associations
Comme démontré, j'ai dépeint une association avec un rapport de cardinalité plusieurs-à-plusieurs (M: N) entre les types d'entité Customer a et Address ; cette approche offrirait une flexibilité future car, comme vous le savez, un Client peut conserver plusieurs Adresses dans le temps, ou même simultanément, et la même Address peut être partagée par plusieurs Customers .
Un Address particulier peut être utilisé de plusieurs manières par un à plusieurs (1: M) Customers ; par exemple, il peut être défini comme Physical , et/ou il peut être défini pour Shipping , et/ou pour Facturation . Peut-être, la même instance Address peut servir chacun des objectifs susmentionnés en même temps, ou elle peut couvrir deux utilisations tandis qu'une autre Address l'occurrence couvre le reste.
a Dans certains environnements commerciaux, un Client peut être soit un Person ou un Organization (situation qui impliquerait un arrangement légèrement distinct, comme détaillé dans cette réponse sur une structure supertype-sous-type) mais dans le but de fournir un exemple simplifié, j'ai décidé de ne pas inclure cette possibilité ici. Dans le cas où vous devez couvrir cette situation dans votre base de données, le post du lien précédent montre la méthode pour résoudre cette exigence.
Order , Address , CustomerAddress et Rôles d'adresse
Généralement, un Order ne nécessite que deux types de Addresses , un pour Shipping et un pour Billing . De cette façon, la même instance Address pourrait remplir les deux Roles pour un individu Order , mais chaque Role est représenté par la propriété respective, c'est-à-dire ShippingAddressId ou BillingAddressId .
Order est connecté avec Address via le CustomerAddress type d'entité associative avec l'aide de deux CLÉS ÉTRANGÈRES multi-propriétés, c.-à-d.
les deux pointant vers la CustomerAddress CLÉ PRIMAIRE multi-propriété affichée comme
… Qui aide à représenter une règle métier qui stipule que (a) une instance Order doit être liée exclusivement avec (b) Address occurrences précédemment associées au spécifique Client qui a fait cela Order , et jamais avec (c) un aléatoire non - Client - lié Adresse .
History for (1) Address and for (2) the CustomerAddress association
Si vous souhaitez fournir la possibilité de modifier les éléments d'information Address , vous devez suivre toutes les modifications des données. De cette manière, j'ai représenté Address comme un type d'entité "vérifiable" qui maintient son propre AddressHistory .
Étant donné que la nature d'une connexion entre un Customer et un Address peut également subir une ou plusieurs modifications, j'ai également décrit le possibilité de gérer une telle association comme une association "vérifiable" grâce au type d'entité CustomerAddressHistory .
À cet égard, divers facteurs traités dans Q & A n ° 1 et Q & A n ° 2 , - à la fois sur l'activation temporel capacités dans une base de données - sont vraiment pertinentes.
Par conséquent, en termes de diagramme affiché et expliqué ci-dessus, j'ai déclaré la disposition de niveau logique suivante (que vous pouvez adapter pour répondre à vos besoins avec exactitude):
-- 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)
);
Si vous voulez y jeter un œil, je l'ai testé dans ce db <> violon qui fonctionne sur SQL Server 2017.
Les tables History
L'extrait suivant de votre question est très important:
Ce que je recherche, c'est comment configurer mes adresses, donc lorsque je les modifie, la commande n'est pas affectée par le fait qu'un client met à jour son adresse ou déménage.
Les tables AddressHistory
et CustomerAddressHistory
aident à garantir qu'un Order n'est pas affecté par Address modifications, car toutes les lignes "précédentes" doivent être conservées dans la table History
respective et peuvent être interrogées si nécessaire. Les opérations UPDATE et DELETE sur ces deux tables doivent être interdites (essayer de changer l'historique peut même avoir des implications légales négatives).
Interval englobé entre les valeurs contenues dans AddressHistory.CreatedDateTime
Et AddressHistory.AuditedDateTime
Représente l'ensemble Period au cours de laquelle une certaine ligne "passée" Address
a été considérée comme "présente", "actuelle" ou "effective". Des considérations similaires s'appliquent aux lignes CustomerAddressHistory
.
La colonne CustomerAddress.IsActive
BIT (booléen) est destinée à indiquer si une certaine ligne Address
est "utilisable" par une ligne Customer
ou non; Par exemple, si elle est définie sur "false", cela signifierait que a Customer n'utilise plus cela Address et donc il ne peut pas être utilisé pour les nouveaux Orders .
Note : D'un autre côté, j'ai vu certains systèmes dans lesquels chaque fois qu'un nouveau Order est effectuée, les informations Address doivent être saisies (plusieurs fois de manière répétée), et les Address (es) utilisées pour le passé Orders ne sont jamais effacés (par conséquent, les changements Orders ne sont pas affectés par les changements Address )).
Cette ligne de conduite peut indubitablement impliquer des volumes de redondance importants, mais il est possible que - selon les besoins d'information exacts de votre domaine d'activité - puisse fonctionner, vous voudrez peut-être également évaluer ses avantages et ses inconvénients.
Récupération des données
La version "actuelle", "actuelle" ou "effective" d'une occurrence Address doit être contenue comme une ligne dans la table Address
, mais en sélectionnant la précédente " states "d'une Address FROM the AddressHistory
(or from CustomerAddressHistory
) table is easy, et ce peut être un exercice intéressant pour améliorer votre SQL compétences en codage.
En ce qui concerne l'une des situations que vous avez mentionnées dans les commentaires, si vous souhaitez récupérer "l'avant-dernière version" d'une ligne Address
individuelle DE sa AddressHistory
, vous devez prendre en compte la MAX(AddressHistory.AuditedDateTime)
et le AddressHistory.AddressId
qui correspond à la valeur Address.AddressId
particulière à portée de main.
À cet égard - au moins lors de la construction d'une base de données relationnelle -, il est très pratique de définir d'abord le schéma conceptual correspondant (basé sur sur les règles métier ) applicables et après cela déclarer son logique arrangement DDL. Une fois que vous obtenez des versions stables et fiables de ces éléments fondamentaux (qui, bien sûr, peuvent évoluer dans le temps), il est temps d'analyser et de déterminer les meilleures façons de manipuler (via les opérations INSERT, UPDATE, DELETE et SELECT ou leurs combinaisons) la concernant les données.
Perception des utilisateurs finaux, vues et assistance des programmes d'application
Évidemment, au niveau external d'abstraction, Address l'information est perçue (par les utilisateurs finaux) comme faisant partie d'un Order , et il n'y a rien de mal à cela, mais cela ne signifie pas que les modélisateurs doivent concevoir les parties importantes de la base de données en question comme ça. Sur ce point, s'il est nécessaire, par exemple, d'imprimer un "full" Order (très faisable), vous pouvez le "reproduire" à la demande à l'aide d'un quelques opérateurs JOIN et clauses WHERE (compte tenu de la période de validité concernée, etc.) peuvent être fixés dans vues pour une consommation future, en envoyant le jeu de résultats pertinent aux programmes d'application associés qui , à son tour, peut améliorer sa mise en forme si nécessaire.
Bien sûr, le ou les programmes d'application seront également très utiles lors de l'exécution d'un Order ; Par exemple, une fenêtre d'application de bureau/mobile ou une page Web peut:
CustomerAddress.IsActive
) ;CustomerAddress.IsBilling
); etCustomerAddress.IsShipping
);faciliter de cette manière tous les processus impliqués dans l'interface graphique (c'est-à-dire le niveau externe d'abstraction d'un système informatisé).
Vous avez demandé (dans les commentaires maintenant supprimés) quelques conseils sur la documentation de base de données solide; par conséquent, comme pour le matériel théorique , je vous conseille vivement de lire tout le travail écrit par Dr. EF Codd , a Turing Récompense destinataire et, bien sûr, le unique créateur du modèle relationnel de données (peut-être maintenant plus pertinent que jamais). Cette liste comprend certains de ses articles et articles extrêmement influents.
Deux ouvrages importants qui ne figurent pas dans la liste susmentionnée sont, précisément, sa conférence du prix ACM Turing intitulée Relational Database: A Practical Foundation for Productivity , from 1981, et son livre dénommé The Relational Model for Database Management: Version 2 , qui a été publié en 1990.
Sur le plan de la conception conceptual , Definition Integrated for Information Modeling (IDEF1X)) est une technique sérieusement recommandée qui a été défini comme une norme en décembre 1993 par les États-Unis National Institute of Standards and Technology (NIST).
Cette réponse a été compilée à partir des commentaires à la question.
Une solution serait d'utiliser un FK pour la table d'adresses dans la table de commande. Cela vous permettra de voir les adresses qui ont été utilisées pour la commande et de dissocier l'adresse de l'adresse actuelle de l'utilisateur.
Pour que cela fonctionne, vous devez insérer une nouvelle adresse et lier cette nouvelle adresse à la table User. Cela signifie que les adresses sont écrites une seule fois et que la modification est une illusion pour l'utilisateur final. Vous pouvez stocker efficacement l'historique de toutes les adresses auxquelles un utilisateur a été associé en déplaçant l'association de la table User vers une table d'association avec un horodatage. Cela vous donnerait un historique des modifications/adresses et conserverait des données immuables dans la table des adresses.
@ MDCCL a déclaré:
[vous devez] organiser votre structure de base de données avec une table pour conserver les données liées à la commande et une autre table pour conserver les informations d'adresse. Et, oui, vous pouvez certainement avoir une table représentant une relation plusieurs-à-plusieurs entre ces deux types d'entités. Si un utilisateur peut modifier ses attributs d'adresse (s), alors vous devez suivre ces modifications, vous devez donc activer le
AddressHistory
correspondant. Cet article est lié à ce dernier aspect.
MDCCL a également donné un aperçu sur la façon de trouver l'adresse actuelle d'un utilisateur ici:
Afin de récupérer la dernière version d'une table d'historique que vous avez, vous devez prendre en compte la
MAX(AuditedDateTime)
de laAddressId
correspondante. La première étape consiste à modéliser/concevoir vos meilleurs arrangements conceptuels et logiques possibles, la deuxième étape consiste à trouver les moyens appropriés pour INSÉRER, METTRE À JOUR, SUPPRIMER et SÉLECTIONNER vos données.