Dans la mesure où cette question est une continuation de Est-ce que mon implémentation du modèle de conception de type / sous-type (pour les sous-classes mutuellement exclusives) est correcte? , Qui lui - même est une continuation de Je ne sais pas comment transformer une entité variable dans la table relationnelle , je demande: ce que vous exactement essayez d'optimiser? Espace de rangement? Le modèle objet? Complexité des requêtes? Performances des requêtes? Il existe des compromis lors de l'optimisation d'un aspect par rapport à un autre, car vous ne pouvez pas optimiser tous les aspects en même temps.
Je suis entièrement d'accord avec les points de Remus concernant:
- Il y a des avantages et des inconvénients à chaque approche (c.-à-d. Le facteur omniprésent "ça dépend"), et
- La première priorité est l'efficacité du modèle de données (un modèle de données inefficace ne peut pas être corrigé par un code d'application propre et / ou efficace)
Cela dit, le choix auquel vous êtes confronté est entre les éléments suivants, classés par ordre de normalisation le plus faible à la plupart de normalisation:
- promotion de la propriété
E
dans la table de type de base
- le garder dans plusieurs tables de sous-types
- normalisation
E
complète vers une nouvelle table de sous-classe intermédiaire au même niveau que C
celui-ci A
et B
sera directement des sous-classes de ( réponse de @ MDCCL )
Examinons chaque option:
Déplacer la propriété E
vers la table de type de base
Avantages
- Réduction de la complexité des requêtes pour les requêtes qui ont besoin
E
mais pas X
, Y
ou Z
.
- Potentiellement plus efficace pour les requêtes qui ont besoin
E
mais pas X
, Y
ou Z
(en particulier les requêtes agrégées) en raison de l'absence de JOIN.
- Possibilité de créer un index
(D, E)
(et si oui, potentiellement un index filtré sur (D, E)
où EntityType <> C
, si une telle condition est autorisée)
Les inconvénients
- Impossible de marquer
E
commeNOT NULL
- Besoin supplémentaire
CHECK CONSTRAINT
sur la table de type de base pour s'assurer que E IS NULL
lorsque EntityType = C
(bien que ce ne soit pas un problème énorme)
- Besoin d'éduquer les utilisateurs du modèle de données pour savoir pourquoi cela
E
doit être NULL
, et devrait même être ignoré complètement, lorsque EntityType = C
.
- Un peu moins efficace quand
E
est un type de longueur fixe, et une grande partie des lignes sont pour EntityType de C
(c. -à ne pas utiliser d' E
où il est NULL
), et ne pas utiliser soit l' SPARSE
option sur la colonne ou la compression des données sur l'index cluster
- Potentiellement moins efficace pour les requêtes qui n'en ont pas besoin
E
car la présence de E
dans la table de type de base augmentera la taille de chaque ligne, ce qui diminuera à son tour le nombre de lignes pouvant tenir sur une page de données. Mais cela dépend fortement du type de données exact de E
, le FILLFACTOR, du nombre de lignes dans la table de type de base, etc.
Conserver la propriété E
dans chaque table de sous-type
Avantages
- Un modèle de données plus propre (c.-à-d. Ne pas avoir à se soucier d'éduquer les autres quant à la raison pour laquelle la colonne
E
dans la table de type de base ne doit pas être utilisée car "elle n'est vraiment pas là")
- Ressemble probablement plus étroitement au modèle objet
- Peut marquer la colonne comme
NOT NULL
s'il s'agissait d'une propriété obligatoire de l'entité
- Pas besoin de supplément
CHECK CONSTRAINT
sur la table de type de base pour s'assurer que E IS NULL
lorsque EntityType = C
(bien que ce ne soit pas un gain énorme)
Les inconvénients
- Requiert JOIN pour sous-type Table (s) pour obtenir cette propriété
- Potentiellement légèrement moins efficace en cas de besoin
E
, en raison de JOIN, selon le nombre de lignes de A
+ que B
vous avez par opposition au nombre de lignes C
.
- Un peu plus difficile / complexe pour les opérations qui traitent uniquement avec des entités
A
et B
(et non C
) comme étant du même "type". Bien sûr, vous pouvez résumer cela via une vue qui fait UNION ALL
entre une SELECT
des tables JOINed pour A
et une autre SELECT
des tables JOINed pour B
. Cela réduira la complexité des requêtes SELECT mais pas si utile pour les requêtes INSERT
et UPDATE
.
- Selon les requêtes spécifiques et la fréquence à laquelle elles sont exécutées, cela pourrait être une inefficacité potentielle dans les cas où avoir un index sur
(D, E)
aiderait vraiment une ou plusieurs requêtes fréquemment utilisées, car elles ne peuvent pas être indexées ensemble.
Normaliser E
vers la table intermédiaire entre la classe de base et A
&B
(Veuillez noter que j'aime la réponse de @ MDCCL comme alternative viable, selon les circonstances. Ce qui suit n'est pas censé critiquer strictement cette approche, mais comme un moyen d'ajouter une certaine perspective - la mienne, bien sûr - en évaluant dans le même contexte que les deux options que j'avais déjà proposées. Cela permettra de clarifier plus facilement ce que je considère comme la différence relative entre la normalisation complète et l'approche actuelle de normalisation partielle.)
Avantages
- le modèle de données est entièrement normalisé (il ne peut y avoir rien de mal en soi, étant donné que c'est ce que les SGBDR sont conçus pour faire)
- complexité des requêtes réduite pour les requêtes nécessitant
A
et B
, mais pas C
(c.-à-d. pas besoin de deux requêtes jointes via UNION ALL
)
Les inconvénients
- un peu plus d'espace occupé (le
Bar
tableau duplique l'ID, et il y a une nouvelle colonne, BarTypeCode
) [négligeable, mais quelque chose à savoir]
- légère augmentation de la complexité des requêtes car un supplément
JOIN
est nécessaire pour accéder à A
ouB
- augmentation de la surface de verrouillage, principalement sur
INSERT
( DELETE
peut être gérée implicitement via le marquage des clés étrangères comme ON CASCADE DELETE
) puisque la transaction sera maintenue ouverte un peu plus longtemps sur la table de la classe de base (c.-à-d. Foo
) [négligeable, mais quelque chose à savoir]
aucune connaissance directe du type réel - A
ou B
- dans le tableau de la classe de base Foo
; il ne sait de type Br
qui peut être A
ou B
:
Autrement dit, si vous devez effectuer des requêtes sur les informations de base générales mais que vous devez soit les classer par type d'entité, soit filtrer un ou plusieurs types d'entités, la table de la classe de base n'a pas suffisamment d'informations, auquel cas vous devez LEFT JOIN
la Bar
table. Cela réduira également l'efficacité de l'indexation de la FooTypeCode
colonne.
aucune approche cohérente pour interagir avec A
& B
vs C
:
Autrement dit, si chaque entité se rapporte directement à la table de classe de base de sorte qu'il n'y a qu'un seul JOIN pour obtenir l'entité complète, alors tout le monde peut acquérir plus rapidement et facilement une familiarité en termes de travail avec le modèle de données. Il y aura une approche commune des requêtes / procédures stockées qui les rend plus rapides à développer et moins susceptibles d'avoir des bogues. Une approche cohérente permet également d'ajouter plus rapidement et plus facilement de nouveaux sous-types à l'avenir.
potentiellement moins adaptable aux règles métier qui évoluent dans le temps:
Cela signifie que les choses changent toujours et il est assez facile de passer E
à la table de classe de base si elle devient commune à tous les sous-types. Il est également assez facile de déplacer une propriété commune vers les sous-types si des changements dans la nature des entités en font un changement valable. Il est assez facile de diviser un sous-type en deux sous-types (il suffit de créer une autre SubTypeID
valeur) ou de combiner deux ou plusieurs sous-types en un seul. Inversement, que se passerait- E
il si plus tard devenait une propriété commune à tous les sous-types? Ensuite, la couche intermédiaire de la Bar
table n'aurait aucun sens et la complexité supplémentaire n'en valait pas la peine. Bien sûr, il est impossible de savoir si un tel changement se produirait dans 5 ou même 10 ans, donc le Bar
tableau n'est pas nécessairement, ni même très probablement, une mauvaise idée (c'est pourquoi j'ai dit " potentiellement moins adaptable"). Ce ne sont que des points à considérer; c'est un pari dans les deux sens.
regroupement potentiellement inapproprié:
Cela signifie simplement que la E
propriété est partagée entre les types d'entité A
et B
ne signifie pas cela A
et B
doit être regroupée. Ce n'est pas parce que les choses «se ressemblent» (c'est-à-dire les mêmes propriétés) qu'elles sont identiques.
Sommaire
Tout comme pour décider si / quand dénormaliser, la meilleure façon d'aborder cette situation particulière dépend de considérer les aspects suivants de l'utilisation du modèle de données et de s'assurer que les avantages l'emportent sur les coûts:
- combien de lignes vous aurez pour chaque EntityType (regardez au moins 5 ans plus tard, en supposant une croissance supérieure à la moyenne)
- combien de Go chacune de ces tables (type de base et sous-types) sera-t-elle dans 5 ans?
- quel type de données spécifique est une propriété
E
- est-ce une seule propriété ou y a-t-il quelques, voire plusieurs, propriétés
- quelles requêtes dont vous aurez besoin
E
et à quelle fréquence elles seront exécutées
- les requêtes dont vous aurez besoin qui n'ont pas besoin
E
et à quelle fréquence elles seront exécutées
Je pense que j'ai tendance à rester par défaut E
dans les tables de sous-types séparées car c'est, à tout le moins, "plus propre". J'envisagerais de passer E
à la table de type de base IF: la plupart des lignes n'étaient pas pour EntityType de C
; et le nombre de rangées était au moins dans les millions; et j'exécute plus souvent qu'autrement les requêtes qui ont besoin E
et / ou les requêtes qui bénéficieraient d'un index lors d' (D, E)
une exécution très fréquente et / ou nécessitent suffisamment de ressources système de sorte que l'index réduit l'utilisation globale des ressources, ou du moins empêche des augmentations de la consommation des ressources qui dépassent les niveaux acceptables ou qui durent assez longtemps pour provoquer un blocage excessif et / ou une augmentation des blocages.
MISE À JOUR
OP a commenté cette réponse :
Mes employeurs ont changé la logique commerciale, supprimant complètement E!
Ce changement est particulièrement important car il est exactement ce que je prédites pourrait arriver dans le « Arnaques » du paragraphe « Normaliser E
sur le tableau intermédiaire entre la classe de base et A
& B
» section ci - dessus (6e point). Le problème spécifique est à quel point il est facile / difficile de remanier le modèle de données lorsque de tels changements se produisent (et ils le font toujours). Certains diront que tout modèle de données peut être refactorisé / modifié, alors commencez par l'idéal. Mais s'il est vrai sur le plan technique que tout peut être refactorisé, la réalité de la situation est une question d'échelle.
Les ressources ne sont pas infinies, pas seulement CPU / Disque / RAM, mais aussi les ressources de développement: temps et argent. Les entreprises fixent constamment des priorités sur les projets car ces ressources sont très limitées. Et assez souvent (du moins d'après mon expérience), les projets visant à gagner en efficacité (même à la fois les performances du système ainsi que le développement plus rapide / moins de bugs) sont prioritaires en dessous des projets qui augmentent les fonctionnalités. Bien que cela soit frustrant pour nous, les techniciens, car nous comprenons les avantages à long terme des projets de refactoring, c'est juste la nature de l'entreprise que les gens d'affaires moins techniques ont plus de facilité à voir la relation directe entre les nouvelles fonctionnalités et les nouvelles. revenu. Cela se résume à: "nous reviendrons pour corriger cela plus tard" == "
Dans cet esprit, si la taille des données est suffisamment petite pour que des modifications puissent être apportées très requête, et / ou vous avez une fenêtre de maintenance qui est assez longue pour non seulement apporter les modifications mais aussi pour annuler si quelque chose se passe mal, puis normalisant E
vers une table intermédiaire entre la table de classe de base et les A
& B
tables sous-classe pourrait fonctionner (bien que les feuilles de toujours vous sans connaissance directe du type spécifique ( A
ouB
) dans la table de classe de base). MAIS, si vous avez des centaines de millions de lignes dans ces tables et une quantité incroyable de code référençant les tables (code qui doit être testé lorsque des modifications sont apportées), alors il est généralement plus pragmatique qu'idéaliste de payer. Et c'est l'environnement que j'ai dû gérer pendant des années: 987 millions de lignes et 615 Go dans la table de classe de base, répartis sur 18 serveurs. Et tellement de code a frappé ces tables (tables de classe de base et de sous-classe) qu'il y avait beaucoup de résistance - principalement de la direction mais parfois du reste de l'équipe - à apporter des changements en raison de la quantité de développement et Ressources d'AQ qui devraient être allouées.
Donc, encore une fois, la "meilleure" approche ne peut être déterminée que situation par situation: vous devez connaître votre système (c'est-à-dire la quantité de données et la façon dont les tables et le code sont tous liés), comment effectuer la refactorisation et les personnes avec qui vous travaillez (votre équipe et éventuellement la direction - pouvez-vous obtenir leur adhésion pour un tel projet?). Il y a quelques changements que je mentionnais et planifiais depuis 1 à 2 ans, et j'ai pris plusieurs sprints / versions pour obtenir peut-être 85% d'entre eux implémentés. Mais si vous n'avez que <1 million de lignes et pas beaucoup de code lié à ces tables, vous êtes probablement en mesure de commencer du côté le plus idéal / "pur" des choses.
N'oubliez pas, quelle que soit la voie que vous choisissez, faites attention au moins au fonctionnement de ce modèle au cours des 2 prochaines années (si possible). Faites attention à ce qui a fonctionné et à ce qui a causé la douleur, même si cela semblait être la plus grande idée de l'époque (ce qui signifie que vous devez également vous permettre d'accepter le vissage - nous le faisons tous - afin que vous puissiez évaluer honnêtement les points douloureux. ). Et faites attention aux raisons pour lesquelles certaines décisions ont fonctionné ou non afin que vous puissiez prendre des décisions qui sont plus susceptibles d'être "meilleures" la prochaine fois :-).
Selon mon interprétation de vos spécifications, vous souhaitez trouver une méthode pour implémenter deux structures supertype-sous-type différentes (mais connectées ) .
Afin d'exposer une approche pour réaliser la tâche susmentionnée, je vais ajouter au scénario en cause les deux types d'entités hypothétiques classiques appelés
Foo
andBar
, que je détaillerai ci-dessous.Règles métier
Voici quelques déclarations qui m'aideront à créer un modèle logique:
A Foo is either one Bar or one C
A Foo is categorized by one FooType
A Bar is either one A or one C
A Bar is classified by one BarType
Modèle logique
Et puis, le modèle logique IDEF1X [1] résultant est illustré à la figure 1 (et vous pouvez également le télécharger depuis Dropbox au format PDF ):
L'ajout de Foo et Bar
Je n'ai pas ajouté
Foo
etBar
pour rendre le modèle plus beau, mais pour le rendre plus expressif. Je considère qu'ils sont importants pour les raisons suivantes:Comme
A
etB
partager l'attribut nomméE
, cette fonctionnalité suggère qu'il s'agit de types de sous-entité d'un type distinct (mais lié) de concept , d' événement , de personne , de mesure , etc., que j'ai représenté au moyen duBar
type de superentité qui, à son tour, est un type de sous-entité deFoo
, qui contient l'D
attribut en haut de la hiérarchie.Puisqu'il
C
ne partage qu'un seul attribut avec le reste des types d'entités en discussion, c'est-à-dire queD
cet aspect insinue qu'il s'agit d'un type de sous-entité d'un autre type de concept , événement , personne , mesure , etc., j'ai donc décrit cette circonstance en vertu de leFoo
type super entité.Cependant, ce ne sont que des hypothèses, et comme une base de données relationnelle est censée refléter avec précision la sémantique d'un certain contexte commercial , vous devez identifier et classer toutes les choses d'intérêt dans votre domaine spécifique afin que vous puissiez, précisément, capturer plus de sens .
Facteurs importants lors de la phase de conception
Il est très utile d'être conscient du fait que, en mettant toute la terminologie de côté, un cluster exclusif de supertype-sous-type est une relation ordinaire. Décrivons la situation de la manière suivante:
Ainsi, il y a une correspondance (ou cardinalité) de un à un (1: 1) dans ces cas.
Comme vous le savez d'après vos publications précédentes, l' attribut discriminateur (colonne, lorsqu'il est implémenté) joue un rôle primordial lors de la création d'une association de cette nature, car il indique l'instance de sous-type correcte avec laquelle le supertype est connecté . La migration de la CLÉ PRIMAIRE de (i) le supertype vers (ii) les sous-types est également d'une importance primordiale.
Structure DDL en béton
Et puis j'ai écrit une structure DDL basée sur le modèle logique présenté ci-dessus:
Avec cette structure, vous évitez le stockage de marques NULL dans vos tables de base (ou relations ), ce qui introduirait une ambiguïté dans votre base de données.
Intégrité, cohérence et autres considérations
Une fois que vous implémentez votre base de données, vous devez vous assurer que (a) chaque ligne de supertype exclusive est toujours complétée par son homologue de sous-type correspondante et, à son tour, garantir que (b) cette ligne de sous-type est compatible avec la valeur contenue dans la colonne de discrimination de supertype . Par conséquent, il est assez pratique d'utiliser ACID
TRANSACTIONS
pour vous assurer que ces conditions sont remplies dans votre base de données.Vous ne devez pas abandonner la solidité logique, l'auto-expressivité et la précision de votre base de données, ce sont des aspects qui rendent décidément votre base de données plus solide.
Les deux réponses précédemment publiées contiennent déjà des points pertinents qui méritent certainement d'être pris en compte lors de la conception, de la création et de la gestion de votre base de données et de ses programmes d'application.
Récupération de données via des définitions VIEW
Vous pouvez configurer certaines vues qui combinent des colonnes des différents groupes de supertype-sous-type , de sorte que vous pouvez récupérer les données à portée de main sans, par exemple, écrire les clauses JOIN nécessaires à chaque fois. De cette façon, vous pouvez sélectionner directement à partir de la VUE (une relation dérivée ou une table ) d'intérêt avec facilité.
Comme vous pouvez le voir, "Ted" Codd était, sans aucun doute, un génie. Les outils qu'il a légués sont assez solides et élégants et, bien sûr, bien intégrés les uns aux autres.
Ressources associées
Si vous souhaitez analyser une base de données étendue qui implique des relations supertype-sous-type, vous trouverez utile les réponses extraordinaires proposées par @PerformanceDBA aux questions de débordement de pile suivantes:
Base de données historique / vérifiable .
[O] ne table ou plusieurs pour de nombreux événements différents mais en interaction? , qui comprend des sous-types imbriqués .
Remarque
1. La définition d'intégration pour la modélisation de l'information ( IDEF1X ) est une technique de modélisation de données hautement recommandable qui a été établie comme norme en décembre 1993 par le National Institute of Standards and Technology ( NIST ) des États-Unis . Il est solidement fondé sur (a) les premiers éléments théoriques rédigés par le Dr EF Codd; sur (b) la vue Entité-Relation des données, développée par le Dr PP Chen ; et aussi sur (c) la technique de conception de bases de données logiques, créée par Robert G. Brown. Il convient de noter que IDEF1X a été formalisé au moyen d'une logique de premier ordre.
la source
E
complètement! La raison pour laquelle j'accepte la réponse de l' utilisateur srutzky est qu'il fournit de bons points qui m'aident à prendre la décision de choisir l'itinéraire le plus efficace. Sinon, j'accepterais votre réponse. J'ai voté votre réponse plus tôt. Merci encore!