Arjuna Aircraft Ident.: F-MBSD | Bonjour, ce topic a pour but de présenter certaines astuces qui permettent de créer des modèles de données génériques.
Qu'est-ce que j'entends par généricité ?
Selon moi, un modèle des données générique permet, sans modifier le modèle (ajout de tables/colonnes) de modifier l'intégralité des informations stockées dans une base de données, ainsi que leurs relations.
C'est de la dénormalisation de Merise/UML ?
Oui et non. C'est de la dénormalisation dans le sens ou les tables vont stocker diverses informations, qui après une analyse classique Merise ou UML auraient dûes être stockées dans des tables différentes. Cependant, en poussant plus loin l'analyse, on peut isoler des similitudes entre ces données, et les regrouper sous une appelation générique. Il en résulte que la modélisation effectuée reste conforme à Merise notamment. Dans ce topic je me base sur l'architecture de la base de données d'un petit ERP sur lequel je travaille, dont la base est certifiée Merise niveau 3.
Un exemple d'utilité
Si on vous demande de gérer dans une base des clients, des produits des dépots et des groupements de clients, vous pouvez créer une table pour chaque type d'information :
- Table "client"
- Table "client_adresse"
- Table "groupement_de_clients"
- Table "groupement_de_clients_adresse"
- Table "depot"
- Table "depot_adresse"
- Table "produit"
Avec dans chacune de ces tables des informations FK qui permettent de lier nu produit à un dépot, un client à une famille de clients, etc.
Mise en place de la généricité ; pourquoi
Si demin on vous demande en plus d'associer un client à des dépôts afin qu'il n'accède qu'à certains produits, vous allez devoir créer une table de correspondance, et des FK en plus. Idem si on vous demande d'intégrer une notion de fournisseurs par produit et par dépot.
Ainsi, l'intégralité de votre code qui exploite la base de données risque d'être impacté par cette modification.
Je vous propose, pour cet exemple simple, de créer seulement quatre tables : tiers / adresses / relation (qui/quoi) / sous-relation (qui/quoi/que)
La table tiers va stocker :
- Les clients
- Les familles
- Les dépots
- Les produits
- Les fournisseurs
Pourquoi ce regroupement ? Tous ces éléments sont identifiés grâce à un ID, et on un NOM, plus quelques informations qui peuvent être identiques (pays d'origine, actif, etc.)
La table des adresses va stocker :
- Les adresses des clients
- Les adresses des groupements de clients
- Les adresses des dépôts
- Les adresses des fournisseurs
Je pense que je n'ai pas besoin d'expliquer ce que ces informations ont en commun
La table des relations va stocker les relations entre deux tiers :
- Client / Groupement de clients
- Client / dépôt
- Produit / dépot
La table des sous-relations va stocker les relations entre trois tiers :
- Dépôt / Produit / Fournisseur
Evidement, ces tables risquent de faire poubelle si on ne peux pas différencier un produit d'un client par exemple. Ainsi, dans la table des tiers, on va utiliser une clé primaire composée : un ID et un type de tiers. Cette clé sera présente dans chaqueune des autres tables, afin de différencier les adresses et les relations.
Structure de la table TIERS :
- SIGTIE (Sigle Tiers : ID)
- TYPTIE (Type de Tier : CLI : Client / GRC : Groupement de clients / PRO : Produit / DEP : Dépôt / FOU : Fournisseur
- NOMTIE : Nom du tiers
- ACTIF : Indique si le tiers est actif ou non (Y/N)
Structure de la table ADRESSE :
En plus de la double clé héritée de la table des tiers, j'ajoute une seconde double clé TYPADR/NUMADR qui permet de stocker plusieurs adresses de types différents pour un même tiers. Par exemple, plusieurs adresses de livraison possible pour un même client, plusieurs adresses commerciales pour un fournisseurs implémenté proche de puisieurs déots.
- TYPTIE
- SIGTIE
- TYPADR (type d'adresse : COM : Commerciale / FAC : Facturation / LIV : Livraison)
- NUMADR (numéro de l'adresse, auto incrément)
- CONTACT
- NUMRUE
- NOMRUE
- CP
- VILLE
- TEL
- FAX
- EMAIL
La table des relations :
En plus de la double clé (TYPQUI/SIGQUI/TYPQUOI/SIGQUOI) j'ajoute un numéro de relation, qui est un auto-incrément. Ce sera utile pour la table des sous-relations.
- NUMREL (numéro unique)
- TYPQUI (Type du tiers origine)
- SIGQUI (Sigle du tiers origine)
- TYPQUOI (Type du tiers cible)
- SIGQUOI (Sigle du tiers cible)
Pour associer un produit "1" aux dépots "5" et "8", on fera par exemple :
INSERT INTO RELATION VALUES ('PRO', 1, 'DEP', 5); -- ID AUTO = 1
INSERT INTO RELATION VALUES ('PRO', 1, 'DEP', 8); -- ID AUTO = 2
La table des sous-relations.
Afin d'optimiser l'espace, au lien de reprendre la première relation qui/quoi, on va la créer dans la table des relations, et réutiliser son NUMREL. On n'a donc plus qu'à le lier au QUE. Voici la structure :
- NUMREL (Relation d'origine)
- TYPQUE (Type du tiers cible)
- SIGQUE (Sigle du tiers cible)
Pour associer par exemple le produit 1 du dépot 5 au fournisseur "2", et le produit 1 du dépôt 8 au fournisseur "7", on fera :
INSERT INTO SRELATION VALUES (1, 'FOU', 2);
INSERT INTO SRELATION VALUES (2, 'FOU', 7);
Note; Si au lieu d'associer un produit d'un dépot à un fournisseur, on veut associer un poduit directement à un fournisseur, il suffit de ne pas créer cette sous-relation, et créer la relation suivante :
INSERT INTO RELATION VALUES ('PRO', 2, 'FOU', 12);
=> Ainsi, le produit 2 est lié au fournisseur 12 quelquesoit le dépôt. On peut aussi lier un fournisseur à un dépôt de la même manière, toujours sans modifier le modèle des données.
On peut optimiser les deux tables de relation en utilisant une table "relationtype", qui permet d'indiquer pour un TYPREL quel typqui est lié avec quel typquoi et quel typque Seulement, cela rends la lecture des tables moins claire. Par contre, cela permet d'augmenter la généricité du code qui est derrière, en permettant la construction de requête dynamiques, simplement en lisant au préalable dans cette table comment retrouver la bonne relation. Perso, je préfère utiliser d'autres systèmes pour rendre le code génériques (écriture de "toutes les requêtes possible" et lecture dans une table de paramètre laquelle utiliser. Dans tous les cas, vous pouvez toujours utiliser des requêtes en dur.
Exploitation
Maintenant qu'on a construit cette usine à gaz, un exemple pratique.
Le client 1 a commandé le produit 5. Comme on ne l'a plus en stock, on recherche l'adresse du fournisseur à contacter pour passer une commande de réaprovisionnement.
Je pars du principe "le plus complexe" : un client est associé à un dépôt, et les produits de chaque dépôt est associé à un fournisseur différent.
Dans un premier temps, on recherche les dépôts auxquel est associé le client :
select dep.id, dep.nom
from tiers dep, tiers cli, relation clidep
where cli.typtie = 'CLI'
and cli.sigtie = 1
and clidep.typqui = cli.typtie
and clidep.sigqui = cli.sigtie
and clidep.typquoi = 'DEP'
and dep.typtie = clidep.typquoi
and dep.sigtie = clidep.sigquoi
|
Maintenant qu'on a cette liste, il faut la filtrer avec la liste des dépots qui permettent de stocker le produit 5.
select dep.id, dep.nom
from relation prodep, tiers dep, tiers cli, relation clidep
where cli.typtie = 'CLI'
and cli.sigtie = 1
and clidep.typqui = cli.typtie
and clidep.sigqui = cli.sigtie
and clidep.typquoi = 'DEP'
and dep.typtie = clidep.typquoi
and dep.sigtie = clidep.sigquoi
and pro.typtie = 'PRO'
and pro.sigtie = 5
and prodep.typqui = pro.typtie
and prodep.sigqui = pro.sigtie
and prodep.typquoi = dep.typtie
and prodep.sigquoi = dep.sigtie
|
Enfin, on recherche la liste des fournisseurs qui approfisiennent les dépots trouvé avec le produit 5.
select fou.id, fou.nom
from srelation prodepfou, tiers fou, tiers pro, rel prodep, tiers dep, tiers cli, relation clidep
where cli.typtie = 'CLI'
and cli.sigtie = 1
and clidep.typqui = cli.typtie
and clidep.sigqui = cli.sigtie
and clidep.typquoi = 'DEP'
and dep.typtie = clidep.typquoi
and dep.sigtie = clidep.sigquoi
and pro.typtie = 'PRO'
and pro.sigtie = 5
and prodep.typqui = pro.typtie
and prodep.sigqui = pro.sigtie
and prodep.typquoi = dep.typtie
and prodep.sigquoi = dep.sigtie
and fou.typtie = 'FOU'
and prodepfou.numrel = prodep.numrel
and prodepfou.typque = fou.typtie
|
Et enfin, on cherche les adresses commerciales 'COM' de chacun de ces fournisseurs.
select fou.nomtie, adr.contact, adr.numrue, adr.cp, adr.ville, adr.fax, adr.email
from adresse adr, srelation prodepfou, tiers fou, tiers pro, rel prodep, tiers dep, tiers cli, relation clidep
where cli.typtie = 'CLI'
and cli.sigtie = 1
and clidep.typqui = cli.typtie
and clidep.sigqui = cli.sigtie
and clidep.typquoi = 'DEP'
and dep.typtie = clidep.typquoi
and dep.sigtie = clidep.sigquoi
and pro.typtie = 'PRO'
and pro.sigtie = 5
and prodep.typqui = pro.typtie
and prodep.sigqui = pro.sigtie
and prodep.typquoi = dep.typtie
and prodep.sigquoi = dep.sigtie
and fou.typtie = 'FOU'
and prodepfou.numrel = prodep.numrel
and prodepfou.typque = fou.typtie
and adr.typtie = fou.typtie
and adr.sigtie = fou.sigtie
and adr.typadr = 'COM'
|
Hop, on a la liste des différentes adresses de tous les fournisseurs qui vont permettre de nous réapprovisionner en fonction de notre besoin. On peut ajouter à cette requête l'adresse des dépôts que chaque fournisseur peut livrer, etc.
Le principal avantage de ce système, c'est que si demain le mode d'attachement des fournisseurs aux produits/dépot change, vous n'avez rien à à changer dans structure de la base, mais simplement les données à re-remplir correctement (ça peut même se faire de façon automatique avec des requêtes en parant des données existantes), et la requête ci-dessus à modifier pour par exemple lier directement le fournisseur au produits.
Ce système permet aussi de gérer du 1,1 avec la même représentation que du n,n, ainsi, ce genre de variances dans les règles de gestion n'impacte en rien ni le modèle ni les requêtes.
De la même façon, ce modèle est déjà près à stocker des KIT (produits composés), des familles de produit, etc.
En rajoutant un champ "RELVAL" dans les tables relation / sous-relation, on pourra stocker aussi des LOT de produits, des délais de réaprovisionnement, etc.
Bref, cette représentation doit très certainement avoir des limites, mais pour le moment, je n'en ai rencontré aucune.
Par contre, il nécessire mûre réflexion, notamment pour ce qui est de la définition des clés qui/quoi(/que)
Ce "mini-cours" est basé sur mon expérience personnelle. Je ne me vente pas d'être un grand gourou en la matière, et il existe peut-être (certainement) des façon encore plus puissantes de gérer ce type d'information (notamment pour ce qui est des relations, ce qui est actuellement le point le plus faible de ce système (en effet, si on veut gérer un qui/quoi/que/où, on est bloqué, à moins d'utiliser des bidouilles plus ou moins propres (tiers fictifs basés sur le relnum d'une relation qui/quoi afin de rajouter un niveau par exemple)
Tout commentaire est le bienvenu. Posez vos questions et problèmes d'implémentation, et je m'efforcerai de vous répondre du mieu que je peux. Message édité par Arjuna le 08-08-2004 à 19:32:34
|