Une nouvelle exigence est apparue sur une ancienne base de code, qui permet essentiellement une communication directe (interne) entre deux classes d'utilisateurs autrefois non directement liées (stockées dans des tables différentes avec un schéma complètement différent et, malheureusement, le code est à peine conscient de l'OO, beaucoup moins conçu, donc il n'y a pas de classe parent). Puisque nous sommes prêts à accrocher un sac à cette ancienne configuration qui n'a jamais considéré cette fonctionnalité, il n'y a aucune garantie qu'il n'y a pas de collisions PK - étant donné l'ensemble de données utilisé, il est pratiquement garanti qu'il y en a.
Donc, la solution semble évidente: tuez-la avec le feu et réécrivez tout le désordre Une table de mappage. J'ai obtenu deux directions pour les manières possibles de mettre en œuvre la carte, mais je ne suis pas un DBA, donc je ne sais pas s'il y a des avantages et des inconvénients que j'ai manqués.
Pour clarifier l'abstraction, considérons trois groupes de données utilisateur disparates: professeurs, administration, étudiants (non, ce n'est pas un devoir. Promesse!)
Cartographie 1
(professor_id, admin_id et student_id sont des clés étrangères à leurs tables respectives)
| mailing_id (KEY) | professor_id | admin_id | student_id |
-------------------------------------------------------
| 1001 | NULL | 87 | NULL |
| 1002 | 123 | NULL | NULL |
| 1003 | NULL | NULL | 123 |
Le +/- de cette approche semble assez lourd sur les inconvénients:
- Deux champs "perdus" par ligne
- Violent 2NF
- Vulnérable à l'insertion / mise à jour des anomalies (une ligne avec seulement 0-1 champ défini NULL, par exemple)
Les pros ne sont pas sans mérites, cependant:
- Le mappage peut être accompli avec une seule recherche
- Déterminez facilement les données "source" pour un utilisateur donné à partir du mailing_id
À vrai dire, dans mon instinct, je n'aime pas du tout cette idée.
Cartographie 2
(supposez que MSG_ * sont des constantes définies, des types d'énumération ou un autre identifiant approprié)
| mailing_id (KEY) | user_type (UNIQUE1) | internal_id (UNIQUE2)|
------------------------------------------------------------------
| 1001 | MSG_ADMIN | 87 |
| 1002 | MSG_PROF | 123 |
| 1003 | MSG_STUDENT | 123 |
Avec cette configuration et un index composite unique de {user_type, internal_id}, les choses deviennent beaucoup plus propres, 3NF est maintenu et le code d'application n'a pas à vérifier les anomalies d'E / S.
À la baisse, il y a un peu de transparence dans la détermination des tables source utilisateur qui doivent être gérées en dehors de la base de données, ce qui revient essentiellement à un mappage au niveau de l'application des valeurs user_type vers les tables. En ce moment, je penche (assez fortement) vers cette 2ème cartographie, car l'inconvénient est plutôt mineur.
MAIS je suis douloureusement conscient de mes propres limites, et je suis sûr que j'ai probablement manqué des avantages ou des pierres d'achoppement dans les deux sens, alors je me tourne vers des esprits plus sages que les miens.
la source
Réponses:
Votre deuxième idée est la bonne. Cette approche vous permet de faire tout le mappage que vous devez faire afin d'intégrer vos trois espaces clés en collision.
Plus important encore, cela permet à la base de données d'imposer la majeure partie de la cohérence dont vous avez besoin en utilisant des contraintes déclaratives .
Vous avez déjà plus de code que vous n'en souhaitez, alors n'ajoutez pas plus de code que nécessaire pour garder votre liste de clés intégrée cohérente. Laissez votre moteur de base de données faire ce pour quoi il a été conçu.
L '«enfant à problème» qui vous gêne dans Mapping 2 est la
USER_TYPE
colonne. Cette colonne est importante car vous en avez besoin pour vous assurer qu'elleINTERNAL_ID
n'apparaît au maximum qu'une fois par type d'utilisateur. La seule fois où vous avez besoin d'un code qui est même au courantUSER_TYPE
est le code qui insère et supprime de votre table de mappage. Cela peut être assez bien localisé. Je suppose que vous allez créer un seul point dans votre code où le contenu de la table de mappage est conservé. Une colonne supplémentaire à cet endroit où les données sont écrites n'est pas un gros problème. Ce que vous voulez vraiment éviter, c'est d'ajouter la colonne supplémentaire partout où les données sont lues .Le code de vos sous-applications qui doit utiliser le mappage peut être parfaitement ignorant du
USER_TYPE
simplement en donnant à chaque sous-application une vue qui filtre les mappages vers le bas pour le type d'utilisateur spécifique à l'application.la source
Par expérience, ma recommandation est de choisir la cohérence plutôt que l'élégance ou les «meilleures pratiques». C'est pour correspondre à la conception existante et aller avec TROIS tables de diffusion (une pour chaque rôle) avec une
mailing_id, user_id
structure de champ simple .C'est inélégant mais ça a quelques avantages ...
Je suis sûr que beaucoup d'autres seront en désaccord avec cette approche, mais les principaux objectifs de la normalisation et des meilleures pratiques sont de rendre le code plus cohérent afin qu'il soit plus facile à suivre et à déboguer ... et évidemment, mettre à jour l'ensemble de la base de code n'est probablement pas réalisable.
la source