J'ai du mal à trouver exactement comment placer de bonnes limites pour quand et où utiliser les tables de recherche dans une base de données. La plupart des sources que j'ai consultées disent que je ne peux jamais en avoir trop mais, à un moment donné, il semble que la base de données soit décomposée en tant de morceaux que, même si elle est efficace, elle n'est plus gérable. Voici un exemple de ce avec quoi je travaille:
Disons que j'ai une table appelée Employés:
ID LName FName Gender Position
1 Doe John Male Manager
2 Doe Jane Female Sales
3 Smith John Male Sales
Imaginez un instant que les données sont plus complexes et contiennent des centaines de lignes. La chose la plus évidente que je vois qui pourrait être déplacée vers une table de recherche serait Position. Je pourrais créer une table appelée Positions et coller les clés étrangères de la table Positions dans la table Employés de la colonne Position.
ID Position
1 Manager
2 Sales
Mais jusqu'où puis-je continuer de décomposer les informations en tables de recherche plus petites avant qu'elles ne deviennent ingérables? Je pourrais créer une table de genre et avoir 1 correspond à Homme et 2 correspond à Femme dans une table de recherche séparée. Je pourrais même mettre des LNames et FNames dans des tableaux. Toutes les entrées "John" sont remplacées par une clé étrangère de 1 qui pointe vers la table FName qui indique qu'un ID de 1 correspond à John. Si vous descendez trop loin dans ce lapin comme celui-ci, votre table Employés est alors réduite à un désordre de clés étrangères:
ID LName FName Gender Position
1 1 1 1 1
2 1 2 2 2
3 2 1 1 2
Bien que cela puisse ou non être plus efficace pour un serveur à traiter, cela est certainement illisible pour une personne normale qui essaie de le maintenir et rend plus difficile pour un développeur d'applications essayant d'y accéder. Donc, ma vraie question est de savoir jusqu'où est trop loin? Existe-t-il quelque part des "meilleures pratiques" pour ce genre de choses ou un bon ensemble de directives? Je ne trouve aucune information en ligne qui définit vraiment un bon ensemble de directives utilisables pour ce problème particulier que j'ai. La conception de la base de données est un vieux chapeau pour moi, mais la BONNE conception de la base de données est très nouvelle, donc des réponses trop techniques peuvent être sur ma tête. Toute aide serait appréciée!
Mais jusqu'où puis-je continuer de décomposer les informations en tables de recherche plus petites avant qu'elles ne deviennent ingérables? Je pourrais créer une table de genre et avoir 1 correspond à Homme et 2 correspond à Femme dans une table de recherche séparée.
Vous mélangez deux problèmes différents. Un problème est l'utilisation d'une table de "recherche"; l'autre est l'utilisation de clés de substitution (numéros d'identification).
Commencez avec ce tableau.
ID LName FName Gender Position
1 Doe John Male Manager
2 Doe Jane Female Sales
3 Smith John Male Sales
Vous pouvez créer une table de "recherche" pour des positions comme celle-ci.
create table positions (
pos_name varchar(10) primary key
);
insert into positions
select distinct position
from employees;
alter table employees
add constraint emp_fk1
foreign key (position)
references positions (pos_name);
Votre table d'origine ressemble exactement à ce qu'elle était avant de créer la table de "recherche". Et la table des employés ne nécessite aucune jointure supplémentaire pour en tirer des données utiles et lisibles par l'homme.
L'utilisation d'une table de "recherche" se résume à ceci: votre application a-t-elle besoin du contrôle sur les valeurs d'entrée fournies par une référence de clé étrangère? Si c'est le cas, vous pouvez toujours utiliser une table de "recherche". (Qu'il utilise ou non une clé de substitution.)
Dans certains cas, vous pourrez remplir complètement cette table au moment du design. Dans d'autres cas, les utilisateurs doivent pouvoir ajouter des lignes à cette table au moment de l'exécution. (Et vous devrez probablement inclure certains processus administratifs pour examiner les nouvelles données.) Le sexe, qui a en fait un norme ISO , peut être complètement rempli au moment de la conception. Les noms de rue pour les commandes de produits en ligne internationales doivent probablement être ajoutés au moment de l'exécution.
Dans votre table Employés, je n'aurais qu'une recherche de "Position" car il s'agit d'un ensemble limité de données qui peuvent se développer.
M
ou F
), limité à 2 valeurs, et peut être appliqué avec une contrainte CHECK. Vous n'ajouterez pas de nouveaux genres (en ignorant les conneries d'exactitude politique)Si vous souhaitez ajouter une nouvelle position, vous ajoutez simplement une ligne à la table de recherche. Cela supprime également anomalies de modification des données qui est un point de normalisation
De plus, une fois que vous avez un million d'employés, il est plus efficace de stocker tinyint PositionID que varchar.
Ajoutons une nouvelle colonne "devise du salaire". J'utiliserais ici une table de recherche avec une clé de CHF, GBP, EUR, USD, etc.: je n'utiliserais pas de clé de substitution. Cela pourrait être restreint avec une contrainte CHECK comme le genre, mais il s'agit d'un ensemble limité mais extensible de données comme Position. Je donne cet exemple car j'utiliserais la clé naturelle même si elle apparaît dans un million de lignes de données d'employé malgré le caractère char (3) plutôt que tinyint
Donc, pour résumer, vous utilisez des tables de recherche
La réponse est "ça dépend". Pas très satisfaisant mais il y a beaucoup d'influences qui poussent et tirent le design. Si des programmeurs d'applications conçoivent la base de données, une structure comme celle que vous décrivez fonctionne pour eux car l'ORM cache la complexité. Vous vous arracherez les cheveux lorsque vous rédigerez des rapports et devrez rejoindre dix tables pour obtenir une adresse.
Conception pour l'utilisation, l'utilisation prévue et l'utilisation future probable. C'est là que votre connaissance du processus métier entre en jeu. Si vous concevez une base de données pour une entreprise vétérinaire, il existe des hypothèses raisonnables sur la taille, l'utilisation et les directions des fonctionnalités qui seront très différentes d'une start-up de haute technologie.
Pour réutiliser une citation préférée
Quelque part, il y a le point idéal. D'après mon expérience, avoir un identifiant de clé dans plus d'une table n'est pas un crime aussi grave que certains le pensent si vous ne changez jamais de clé primaire.
Prenez cet exemple abrégé de tables hautement normalisées à partir d'un système réel
CREATE TABLE PROPERTY
(ID NUMBER(9) NOT NULL);
CREATE TABLE PROPERTY_TYPE
(ID NUMBER(9) NOT NULL);
CREATE TABLE PROPERTY_LOCALE
PROPERTY_ID NUMBER(9) NOT NULL,
(LOCALE_ID NUMBER(9) NOT NULL, --language
VALUE VARCHAR2(200) NOT NULL);
CREATE TABLE PROPERTY_DEPENDENCY
(PROPERTY_ID NUMBER(9) NOT NULL,
PARENT_PROPERTY_ID NUMBER(9) ,
PROPERTY_TYPE_ID NUMBER(9) NOT NULL);
Ces tables établissent une liste liée de propriétés uniques et de propriétés enfant parent et elles sont utilisées ici
CREATE TABLE CASE_PROPERTY
(ID NUMBER(9) NOT NULL,
PARENT_ID NUMBER(9),
CASE_ID NUMBER(9) NOT NULL,
PROPERTY_ID NUMBER(9),
PROPERTY_TYPE_ID NUMBER(9) NOT NULL);
Cela semble bien: obtenir tous les cas avec un property_id en une seule sélection
Obtenons une liste à choisir
Select pl.value, pd.property_id
from property_locale pl, property_dependency pd
where pl.property_id = pd.property_id
and pd.property_type_id = 2; --example number
Essayez maintenant de sélectionner toutes les propriétés d'un cas s'il a des property_types de 3 et 4 et 5, ou pas ...
SELECT cp2.case_id,
(SELECT pl.VALUE
FROM case_property cp, property_locale pl
WHERE cp.property_id = pl.property_id
AND CP.PROPERTY_TYPE_ID = 2
AND pl.locale_id = 2
AND cp.case_id = cp2.case_id)
AS VALUE1,
(SELECT pl.VALUE
FROM case_property cp, property_locale pl
WHERE cp.property_id = pl.property_id
AND CP.PROPERTY_TYPE_ID = 34
AND pl.locale_id = 2
AND cp.case_id = cp2.case_id)
AS VALUE2,
(SELECT pl.VALUE
FROM case_property cp, property_locale pl
WHERE cp.property_id = pl.property_id
AND CP.PROPERTY_TYPE_ID = 4
AND pl.locale_id = 2
AND cp.case_id = cp2.case_id)
AS VALUE3
FROM case_property cp2
WHERE cp2.case_id = 10293
Cela fait juste mal ... même lorsque vous utilisez des moyens plus élégants de gérer cela. Cependant, ajoutez un peu de normalisation en décomposant les propriétés pour lesquelles un cas n'aura qu'un seul property_id et cela pourrait être bien mieux.
Pour savoir quand vous avez trop de tables ou pas assez, essayez d'interroger la base de données avec des questions sur l'application, un rapport et une analyse d'année en année utiliseront.