|
Publicité ' | ||||||||||||||||||||||||
|
|
#41 | ||||||
|
Expert Confirmé Sénior
![]() ![]() ![]() François de Sainte MarieSpécialiste en bases de données Inscription : septembre 2006 Messages : 3 637 ![]() |
Bonsoir Daranc,
Vous écrivez Citation:
On va essayer de remédier à cela et de faire en sorte que le mirage se dissipe. Je vous ai demandé de vous rabattre sur le lien "hippique" ("Unicité d'une clef composée") parce que, à l’aide d’un exemple simple mais fort intéressant, on y a traité de la normalisation (par exemple mon message du 20/02/2007, 12h36) : définition des dépendances fonctionnelles, contraintes d’unicité et d’irréductibilité, clés candidates, surclés, forme normale de Boyce-Codd (BCNF), etc. On a montré pourquoi la table T (Jockey, Cheval, Course, Dossard) était en BCNF. Dans mon message du 21/02/2007, 01h09, j’ai rappelé la définition de la Première forme normale (1NF). J’ai mis en garde contre les définitions fantaisistes (21/02/2007, 12h49). J’ai aussi traité de la 2NF (24/02/2007, 00h18). Recommencer tout cela au sein de la discussion " Dénormalisation de table. Quand ?" n’est pas très approprié, car si les concepts sont parfois évoqués (dépendance fonctionnelle), ils sont généralement omis et de toutes façons non formellement définis, tout devra être repris à zéro. D’autre part, le débat y est plutôt : faut-il dénormaliser ? Citation:
Je vous rappelle qu’il existe une traduction en français de l’ouvrage de référence : C.J. Date. Introduction aux bases de données, 8e édition (Vuibert) http://www.amazon.fr/Introduction-ba.../dp/2711748383 Citation:
http://www.almaden.ibm.com/cs/people/fagin/sigmod79.pdf http://www.almaden.ibm.com/cs/people/fagin/ http://www.almaden.ibm.com/cs/people/fagin/papers.html Quant aux conséquences sur le stockage de données, je commence par rappeler, comme je l’ai fait précédemment dans cette discussion, qu’il faut faire une distinction très nette entre le niveau logique et le niveau physique : l’indépendance du premier est totale par rapport au second. A ce jour, on peut voir les choses ainsi, à l’aide d’un exemple très simple. Prenons celui des courses hippiques et considérons la table suivante : Edition (Course_Id, Course_Date_Id, Course_Date, Temps_Vainqueur, Course_Nom, Course_Lieu) La 2NF est violée (cf. la discussion sur ces courses, mon message du 24/02/2007, 00h18). A l’instar du ver de terre, la table doit subir une scissiparité et donner lieu donc à deux tables, celles que l’on a définies dans ce même message et qui sont en BCNF, sachant que par jointure naturelle de ces deux tables, on retrouve la table délinquante, ni plus ni moins (par application du théorème de Heath, voir à la fin de ce message). Course (Course_Id, Course_Nom, Course_Lieu) Edition (Course_Id, Course_Date_Id, Course_Date, Temps_Vainqueur) Si l’on fait référence aux Create Table (toujours mon message du 24/02/2007, 00h18), chaque tuple occupe ici respectivement 16 octets et 68 octets (je ne tiens pas compte des octets dont le système a besoin). En supposant que les volumétries soient les suivantes : Table Course, V1 : 10 000 lignes, Table Edition, V2 : 1 000 000 lignes, L’encombrement global logique est le suivant : V1 * 68 + V2 * 16 = 16 680 000 octets Si l’on considère la table Edition qui viole la 2NF, son encombrement logique est de l’ordre de : (16 + 64) * V2 = 80 000 000 octets. Dans la mesure où la correspondance entre un tuple (logique) et un enregistrement (physique) est de un pour un dans les systèmes actuels : => Un arrangement mathématique peut manifestement avoir des conséquences sur le stockage des données. Les volumétries ne sont peut-être pas très pertinentes concernant des courses hippiques (quoique à l’échelon planétaire...), mais si l’on remplace ces tables par celles que l’on rencontre dans le monde de la banque, de l’assurance, de la retraite, de la sécu, de la DGI, de la téléphonie, j’en passe et des meilleures, on change d’échelle et vous pouvez multiplier par un facteur 100. Il y a quand même un élément dont il faut tenir compte : les mathématiques ne se situent ni dans le temps ni dans l’espace. Au niveau physique, on ne peut pas en dire autant ! En l’occurrence, ce qui était vrai il y a dix, vingt, trente ans ne l’est plus aujourd’hui et ce qui est vrai aujourd’hui fera sourire dans dix, vingt ou trente ans. Ainsi, il est naturel aujourd’hui de compresser les données et les 80 000 000 d’octets n’en font peut-être plus que le tiers : contrairement à hier ou avant-hier, l’encombrement physique n’est plus égal à l’encombrement logique (au moins sur disque, pour ce qui se passe dans les buffers et espaces apparentés, c’est une autre histoire). Et demain ? Si l’on utilise le modèle TransRelational (TM) de Steve Tarin, on dira adieu aux index, les colonnes Course_Nom et Course_Lieu de la table Edition non 2NF comporteront physiquement le même nombre de lignes que la table Course en BCNF. Les DBA de demain auront une pensée émue pour leurs anciens qui projetaient au niveau physique l’image qu’ils avaient des tuples logiques. Attention, le modèle TransRelational (TM) ne remplace pas le Modèle relationnel ! Son rôle est de réaliser la correspondance avec le niveau physique ("Trans" est l’abréviation de "Transform"). Mais quelle révolution sous le capot ! Autrement dit, le gaspillage physique pour cause de non normalisation 2NF, 3NF ou BCNF ne sera plus un argument d’optimisation. Resteront les arguments traditionnels : — Éliminer la redondance inutile. Citation:
En tout état de cause, j’utiliserai les tables que j’avais définies (toujours mon message du 24/02/2007, 00h18). En effet, vous mentionnez "Deauville, 7e", quand de mon côté je mentionne "Prix d’Amérique, édition [de l’année] 1956" (voyez les Insert) : Course (Course_Id, Course_Nom, Course_Lieu) Edition (Course_Id, Course_Date_Id, Course_Date, Temps_Vainqueur) Resultat (Course_Id, Course_Date_Id, Dossard_Id, Jockey_Id, Cheval_Id, Place) Jockey (Jockey_Id, Jockey_Nom) Cheval (Cheval_Id, Cheval_Nom, Cheval_Date_Naissance, Propriétaire) A partir de ces tables, on peut définir une table des résultats, qui rende transparentes les jointures : V_COURS (Course_Nom, Course_Lieu, Cheval_Nom, Jockey_Nom, Dossard_Id, Course Date) Table virtuelle certes, mais table quand même, résultant de la jointure des autres tables : Code :
Si par exemple, vous disposez d’une machine bases de données Teradata, les E/S sont effectuées en parallèle (à charge bien sûr du DBA de s’en assurer). Avec DB2 for z/OS, même principe, vous disposez des tuyaux nécessaires pour paralléliser. Avec ce genre de systèmes, les DBA savent agir pour que l’on ne ressente pas de différence de performances. Je suppose qu’un expert Oracle tiendra un discours de cette nature. Avec mon SQL Server Express sur mon modeste PC avec un seul disque dur, je constaterai peut-être la différence, mais je n’ai pas testé. Quoi qu’il en soit, comme je l’ai déjà écrit dans cette discussion, quand c’est pour de vrai et que j’engage ma société sur la performance de la base de données dont j’ai réalisé l’architecture, je prototype encore et encore, jusqu’à ce que la performance définie par le cahier des charges soit effective. Et croyez-moi, j’ai de sacrés souvenirs à ce sujet. Je peux vous dire que j’ai effectué des jointures impliquant plus de 10 tables (en BCNF bien entendu), chacune comportant des dizaines de millions de lignes : si je n’ai pas eu de problèmes de performance, c’est parce que j’ai toujours commencé par une validation vigilante et sévère des modèles conceptuels de données Entités/Relations, et poursuivi au niveau physique par un choix et une organisation soignés des index et autres ressources, avec tests de performance (et de contention...) très complets, en collaboration avec l’équipe des DBA. Une fois encore, ne mélangeons pas les niveaux. Vous savez peut-être que les SGBD relationnels devancent les autres d’un bon paquet de longueurs, en termes de performance. Mais, quand en 1975, Ted Codd présenta le Modèle relationnel (qui n’existait bien sûr que sur le papier) à des pontes d’IBM, les questions qu’on lui posa furent du genre : — Pourriez-vous fournir la justification économique de solutions d’entreprises. — Une estimation des applications potentielles chez les utilisateurs, par secteur d’activité et par type d’application si possible. — Les méthodes de programmation, leur compatibilité et un comparatif avec notre standard actuel de bases de données, DL/1. J’en passe et des meilleures. Pauvre mathématicien égaré chez les managers... Ses collègues réussirent à obtenir qu’un prototype fut réalisé : System R. La révolution était commencée. Si vous vous sentez toujours victime d’un dysfonctionnement oculaire, précisez ce qui vous gêne plus particulièrement... __________________________________ NB. Théorème de Heath (1971) : Soit la variable relationnelle R (A, B, C) dans laquelle A, B et C sont des ensembles d’attributs de R. Si R satisfait à la dépendance fonctionnelle A -> B, alors R est égale à la jointure de ses projections sur {A,B} et {A,C}. (Le terme "variable relationnelle" peut être traduit informellement par le terme "table").
__________________
_ Faites simple, mais pas plus simple ! (A. Einstein) E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire ») => La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale ») __________________ Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !) |
||||||
|
|
10
|
|
|
#42 |
|
Membre Expert
![]() Inscription : janvier 2007 Messages : 1 290 ![]() |
Bonjour
Je vous remercie de prendre le temps ,de vous penchez sur des problèmes qui avouons le n'ont pas d'existence fondamentale . comme je vous l'avez précisé seule la curiosité m'a amené sur ce débat. Mais avant de vous dire ou je bloque je vais déjà lire (et relire) ce que vous m'avez cité. je tenais à vous rassurer sur ce que vous nommez "certaine amertume" , c'est garder le moral à tous les coups (un peu comme lorsqu'un lumbago vous oblige de descendre à quatre pattes d'un canapé , vous pensez au moins je repère mes pantoufles).Et pour ce qui est des mathématiques les ensembles n'ont pas ce que j'ai préféré (surtout attaqué ça en cinquième et abandonné en quatrième le suivi de l'EN) mais je vais rouvrir mes bouquins . de toute façon je ne reviendrais qu'après avoir essaye de (ou réussi à) comprendre Cordialement Daranc |
|
|
00
|
|
|
#43 |
|
Membre Expert
![]() Inscription : janvier 2007 Messages : 1 290 ![]() |
Bonjour
je crois que pour la normalisation j'ai compris le but (sinon la construction) c'est l'exemple des courses qui me bloquait un peu :(un jockey peut monter le même cheval dans la même course à un an d'intervalle et ça, ça me chagrinais) j'ai en suivant les liens donnés (et en recherchant les définitions des termes employés) trouvé un exemple de bibliothèque. un fournisseur change d'adresse et ce sont tous les livres y référant qu'il faut modifier alors que si il est relié aux livres par une table fournisseurs seul sa fiche fournisseurs est à mettre à jour ;Ce qui prends trois minutes ( compris le temps de parler du film du dimanche, un navet, soir avec la collègue ) autrement une semaine à plein temps pour un petit fournisseur ,sans certitude de ne rien oublier; de même on peut rajouter un fournisseur qui n'a pas encore fournit quoi que ce soit . Bon j'ai pas fini de décortiquer , j'y retourne Cordialement Daranc |
|
|
00
|
|
|
#44 |
![]() ![]() Développeur PHP, .Net, T-SQL Inscription : décembre 2006 Messages : 2 354 ![]() |
Salut
y a rien de plus intelligents que de normailiser une table. une table denormaliser manque de cohérence. normalise jusqu'au BCFN ça ira mieux |
|
|
00
|
|
|
#45 |
|
Membre Expert
![]() Sylvain DevidalChef de projets Générix Inscription : février 2010 Messages : 1 517 ![]() |
Bonjour,
Je viens ajouter un petit grain de sable à l'édifice. Je travaille sur un ERP, qui s'appelle Generix. Et cet outils m'a ouvert les yeux sur une nouvelle façon de modéliser les bases de données. En effet, outre normaliser/dénormaliser, il y a une étape d'abstraction à prendre en compte. En effet, cet ERP par du principe que, dans une entreprise, j'ai par exemple un nombre infini de tiers : Client, Fournisseur, Prospects, Dépôts, Magasins, Centrales d'achat, Vendeurs, etc. Idem pour les "évènements" : habituellement, on a les flux suivants en Gescom, mais il peut y en avoir d'autres : devis, commande, livraison, facture ; avoir ; retour ; que ce soit en achat ou en vente. La normalisation préconise de faire une table par type de tiers (donc une table client, une table magasin, une table vendeur, etc.) Idem avec les évènements. Et c'est vite le bordel : 1/ On a 500 tables avec des structures communes 2/ Le jour où on a besoin d'une nouvelle fonctionnalité (je introduire une notion de carriste dans ma base pour tracer qui a mis quoi où et quand dans mon dépôt), je dois recréer des tables pour gérer mon carriste, et des tables pour gérer la mise en rayon dans mon dépôt. L'abstraction joue alors son rôle ici : - Ben mon carriste, c'est un tiers comme un client ou un magasin - Ben le fait de placer un produit en rayon, c'est exactement comme une livraison ou un avoir fournisseur Et donc après avoir créé 500 entités dans le MCD, on mutualise les tables, en ajoutant des colonnes dans la clé primaire. Dans Generix par exemple, dans la table tiers, la clé est composé de 3 colonnes : CODSOC : Code société (ceci permet à plusieurs entités juridiques d'utiliser la même base de données, avec notions de hiérarchie dans les tables de référentiel) TYPTIE : Type de tiers (CLI = Client, REP = Vendeur, DEP = Dépôt, etc.) SIGTIE : Identifiant du tiers Et pour les événements : CODSOC ACHVTE : Indicateur Achat ou Vente TYPEVE : Type d'évènement (CDE = Commande, FAC = Facture, LIV = livraison, etc.) NUMEVE : Numéro de la commande/livraison/... L'ensemble des éléments venant s'ajouter à la clé composite tels que TYPTIE, ACHVTE, TYPEVE sont gérés dynamiquement dans des tables de correspondances, ce qui permet d'étendre le modèle des données sans créer de nouvelles tables. Et même plus loin : un table permet d'étendre les colonnes d'une table pour une clé donnée, et ainsi d'avoir virtuellement autant d'attributs spécifiques dans chaque table Et une table "fourre tout" permet de stocker dans une structure commune l'ensemble des tables de référence de la base (la liste des ACHVTE/TYPEVE/NUMEVE, mais aussi la description de ce qu'est ma zone 824 sur mon événement de type LIV, et même la liste de valeurs autorisés dans cette zone). En gros, là où normalement, on aurait besoin de plus de 100 tables pour gérer un flux GESCOM standard, Generix n'a besoin en tout et pour tout que... d'une dizaine de tables à tout casser. Ceci a plusieurs avantages : 1/ Simplicité du modèle : un nombre restrein de tables à utiliser 2/ Généricité des requêtes et des binaires : le binaire qui gère un client ou un dépôt par exemple est identique et utilise les mêmes requêtes : seul la valeur du TYPTIE diffère dans les requêtes 3/ Fluidité de la lecture des tables : malgré des alias à la mord-moi-le-noeud, un TYPTIE ou un TYPEVE collé dans une requête permet d'identifier dans la seconde sur quelle "table" on travaille Niveau performances, je n'ai jamais trop comparé, mais je doute très fortement que cela impacte quoi que ce soit, et même au contraire. Donc avant de dénormaliser comme un porc, je pense qu'il est intéressant de faire cette passe d'abstraction du modèle, afin de le simplifier. |
|
|
02
|
|
|
#46 | ||
|
Expert Confirmé Sénior
![]() ![]() ![]() François de Sainte MarieSpécialiste en bases de données Inscription : septembre 2006 Messages : 3 637 ![]() |
Citation:
Votre problème relève de la modélisation : au niveau conceptuel, il est évident qu’en présence de 36 types de tiers, tout concepteur normalement constitué mettra en œuvre une entité-type (ou une classe) TIERS en relation avec une entité-type (ou une classe) TYPE_DE_TIERS. Au niveau tabulaire, ces deux entités-types feront chacune l’objet d’une table. Citation:
Evitez surtout à l’avenir de commettre des erreurs de casting. Vous pouvez donc tranquillement remettre votre grain de sable dans votre seau et réfléchir à ce que l'on entend par modéliser proprement.
__________________
_ Faites simple, mais pas plus simple ! (A. Einstein) E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire ») => La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale ») __________________ Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !) |
||
|
|
10
|
|
|
#47 |
|
Membre Expert
![]() Sylvain DevidalChef de projets Générix Inscription : février 2010 Messages : 1 517 ![]() |
Non, dans le cas que je décrit, SIGTIE peut être identique selon les TYPTIE et les CODSOC.
En ce qui concerne l'erreur de casting, j'admet que je ne suis pas pointilleux sur les mots. En revanche, je ne vais pas me laisser limite insulter de la sorte. La plupart du temps, la dénormalisation est motivée avant tout par une tentative de "simplification" du modèle des données. Les cas de dénormalisation à des fins d'optimisation sont rares, même si c'est cet argument qui ressort lorsqu'on demande pourquoi, car il est plus convainquant qu'un simple "j'en ai marre d'avoir 200 tables dans mes requêtes". Il n'est pas rare de trouver, au moment de la conception, des tables avec une adresse dans la commande. "Ben oui, mais c'est parceque mon client il a déjà une adresse, le fournisseur aussi, et du coup ça m'ennuie de faire une table de plus pour stocker les adresses des livraisons". Ca, c'est un cas de dénormalisation typique, qui est évité sans problème par la phase d'abstraction que j'ai énnoncé, et que nombre de personnes ne font pas : c'est clairement pas à l'école qu'on apprend ça, et encore moins dans les SSII où les développeurs/chef de projets n'ont aucun suivit pédagogique, aucun moyen de capitaliser d'un projet à l'autre. |
|
|
01
|
|
|
#48 | |||||
|
Expert Confirmé Sénior
![]() ![]() ![]() François de Sainte MarieSpécialiste en bases de données Inscription : septembre 2006 Messages : 3 637 ![]() |
Citation:
Si on a l’intuition que la dénormalisation vaut le coup, on met en œuvre un prototype pour comparer les performances et mettre en balance les effets secondaires. Mes travaux de prototypage m’ont toujours montré que l’intuition est en l’espèce bien mauvaise conseillère. Par ailleurs, je n’ai jamais vu quelqu’un programmer une requête faisant mention de 200 tables, et pourtant j’en ai vu des choses bizarres en plus de trente-cinq ans de pratique des bases de données relationnelles (et bien plus si l’on tient compte des bases de données non relationnelles), dans bien des secteurs (banque, assurance, industrie, distribution, retraite, j’en passe et des meilleures), tant dans le domaine de la conception, de l’audit, du conseil, de l’animation des équipes de développement, du rattrapage en catastrophe de projets partant à vau-l’eau. Maintenant, pour reprendre votre exemple des tiers, en supposant qu’il y ait 200 types possibles de tiers et que le concepteur en ait inféré 200 entités-types, donc autant de tables, il est évident qu’il s’agirait là d’une maladresse (euphémisme) due à son incompétence. Citation:
Cela dit, si on n’aime pas développer des requêtes comportant des jointures (puisque c'est elles qui sont manifestement en cause), alors plutôt que dénormaliser, on met en oeuvre des vues qui « encapsulent » ces jointures. Citation:
De votre côté, vous avez votre propre technique d’abstraction, orientée « fourre-tout » comme vous le dites dans votre message précédent (« une table "fourre tout" permet de stocker dans une structure commune »), mais heureusement il y a d’autres techniques, moins ad-hoc, qu’elles soient ascendantes, descendantes ou mixtes. Pour les étudier, je vous recommande la lecture des bons auteurs, tels qu’Yves Tabourier (De l’autre côté de Merise) ou Dominique Nanci (Ingénierie des systèmes d’information : Merise deuxième génération). Incidemment, je fais observer qu'on ne modélise pas dans le but de réduire le plus possible le nombre des entités-types (ou classes) au niveau conceptuel (donc le nombre de tables au niveau tabulaire), mais pour représenter un univers du discours ayant sémantiquement un sens, sans amalgamer des données de types distincts. Citation:
Citation:
Quant aux SSII, il y en a qui font malheureusement l’impasse, reconnaissons-le, mais il ne faut pas généraliser. En ce qui me concerne, j’ai fait partie pendant plus de trente ans de la même SSII ; j’ai évidemment passé le plus clair de mon temps sur le terrain, mais cela ne m’a pas empêché de monter une bonne dizaine de cours et de former quelques centaines de nos ingénieurs (déjà expérimentés), notamment en ce qui concerne la modélisation des bases de données, de former des formateurs et suivre les ingénieurs qui le souhaitaient. Accessoirement, outre nos clients, j’ai même formé la concurrence, comme quoi...
__________________
_ Faites simple, mais pas plus simple ! (A. Einstein) E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire ») => La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale ») __________________ Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !) |
|||||
|
|
30
|
|
|
#49 | |||
|
Membre Expert
![]() Sylvain DevidalChef de projets Générix Inscription : février 2010 Messages : 1 517 ![]() |
Citation:
![]() Ce que je veux dire, c'est que j'ai personnellement très rarement vu des cas de besoin de dénormalisation (je ne parle pas pour un DataWare, je parle pour une base applicative) pour des raisons purement de performances. Par contre, des dénormalisation pour simplifier des développements, améliorer les performances en évitant de faire plusieurs requête pour récupérer un résultat (l'exemple des téléphones est bon : si j'ai 3 téléphone pour une personne, soit je fais une jointure et j'ai 3 lignes par personnes, soit je fais une requête par personne : si c'est pour éditer la liste de toutes les personnes avec un tableau de leurs numéros de téléphones, c'est assez moyen). Voilà, moi c'est ce genre de cas que j'ai principalement vu, poussant des dev à dénormaliser. Citation:
Citation:
Les informations de SIRET, etc. sont bien évitement stockées soit dans la table des tiers (lorsqu'elles sont succeptibles d'être communes à suffisament de types de tiers -il vaut mieux parfois sacrifier une normalisation à l'extrême au profit de la simplicité lorsque la simplicité s'applique au plus grand nombre-) ou dans des tables dépendantes. PS : C'est quoi ta SSII que je signe tout de suite ? |
|||
|
|
00
|
|
|
#50 | ||||||
|
Expert Confirmé Sénior
![]() ![]() ![]() François de Sainte MarieSpécialiste en bases de données Inscription : septembre 2006 Messages : 3 637 ![]() |
Citation:
Je rappelle une fois de plus que lorsqu’on bâtit un prototype pour mesurer les bienfaits que procurerait a priori la dénormalisation quant à la performance, on se rend compte qu'en réalité cette dénormalisation n’apporte rien, sinon des effets secondaires fâcheux. Je me souviens des réactions de la direction de la Production d’une très grande banque, d’abord fort septique quant au principe même du prototypage des performances, puis, après en avoir tâté, l’imposer à l’ensemble des projets : comme dit l’autre, « better prevent than cure ». Toujours dans cette banque, pour le projet dont j’avais validé le modèle, des mois avant que les développements ne commencent on connaissait la performance de la base de données normalisée suite au prototypage. En conséquence aucune dénormalisation. Lors des réponses d’appel d’offre et propositions diverses faites aux DSI, à chaque fois que j’ai eu à engager mon entreprise quant à la performance, jamais je n’ai par exemple fourni des durées de traitements sortant d’un chapeau, je ne suis pas un commercial ; invariablement je me suis seulement engagé à d’abord bâtir un prototype et seulement après le verdict de celui-ci, enfin quantifier pour de vrai pour que le client ait une vue objective. Et n’oublions pas qu’il y a les clauses de pénalité qui sont appliquées en cas de non satisfaction de l’engagement. En outre, s’il s’agit de simplifier les développements, je rappelle ce que j’ai écrit : Citation:
Citation:
Citation:
Citation:
En ce qui concerne la colonne faisant l’objet de l’auto-incrémentation, autrement dit la clé primaire de la table, cette colonne ne sert que pour effectuer les jointures et autres opérations relationnelles, elle doit rester cachée à l’utilisateur, lequel n’est concerné que par les propriétés naturelles, telles que le numéro Siret des tiers. Incidemment, je note que la colonne SIGTIE dont vous faites mention n’est pas identifiante, puisqu’il faut l’associer aux colonnes CODSOC et TYPTIE pour garantir la propriété d’unicité. Citation:
Que signifie cette 2e partie de phrase : « il vaut mieux parfois sacrifier une normalisation à l'extrême au profit de la simplicité lorsque la simplicité s'applique au plus grand nombre » ? Pour ma part, je fais observer que la normalisation des tables et la simplicité font particulièrement bon ménage. Je crains que nous ne donnions pas le même sens au terme « normalisation ». La normalisation des bases de données est une branche des mathématiques appliquées, avec laquelle on s’appuie sur une axiomatique (les axiomes et règles d’Armstrong) et des théorèmes (essentiellement Heath et Fagin), des algorithmes et divers outils théoriques (voyez par exemple The Theory of Relational Databases de David Maier) pour prouver que les tables composant une base de données sont en 2NF, 3NF, BCNF, 4NF, 5NF, 6NF. Si vous l’entendez ainsi, alors vous conviendrez que la 2e partie de votre phrase n’est pas pertinente.
__________________
_ Faites simple, mais pas plus simple ! (A. Einstein) E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire ») => La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale ») __________________ Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !) |
||||||
|
|
50
|
|
|
#51 | |
|
Expert Confirmé
![]() |
Citation:
J'en suis même convaincu.
__________________
Prendre conscience, c'est transformer le voile qui recouvre la lumière en miroir. MCTS Database Development |
|
|
|
10
|
|
|
#52 | |||
|
Membre Expert
![]() Sylvain DevidalChef de projets Générix Inscription : février 2010 Messages : 1 517 ![]() |
Citation:
Dans ce topic, il est question de démontrer que dénormaliser pour améliorer les performances des requêtes est souvent illusoire. Sur ce point, je suis parfaitement d'accord, il est très rare que la suppression d'une jointure permette de changer significativement le temps d'exécution d'une requête, alors que l'alourdissements des tables peut rapidement s'avérer catastrophique. EN REVANCHE, pour reprendre l'exemple des téléphones, si j'ai 3 colonnes téléphone dans ma table, alors en une seule requête, je récupère une ligne par tiers, avec ces trois téléphones. Dans le code de mon application, les SELECT et UPDATE seront alors extrêment rapide et simples : une seule requête, point barre. Maintenant, si j'ai une table de téléphones, j'aurai soit : - Une seule requête qui ramène X lignes par tiers (avec X = nombre de numéro de téléphone), ce qui va effondrer mes performances réseau entre mon serveur SGBD et mon serveur d'application. - Deux requêtes, une pour les tiers, puis autant de requêtes qu'il y a de tiers pour récupérer les numéros de téléphone tiers par tiers : cette fois, on a une très forte dégradation des performances sur la base de données (des centaines de requêtes supplémentaires, même si elles sont simples, sur un index et avec peut de résultat, ça se fait forcément ressentir), et sur mon réseau pour faire transiter tous les résultats des requêtes. - Deux requêtes, une pour récupérer tous les tiers, puis une pour récupérer tous les numéros de téléphones, avec cette fois un parcours des deux curseurs en // dans la même boucle : à nouveau, on a deux requêtes au lieu d'une, donc aucun intérêt au niveau des performances sur le SGBD et le réseau. - Une seule requête avec un nombre figé d'auto-jointures sur de type OUTER JOIN sur mes numéros, afin de reproduire la structure de la table dénormalisée. On perds en performances (même s'il s'agit de jointures sur des index, c'est forcément plus lent que de la lecture séquentielle dans une seule table). => Dans ces quatre cas, en termes de coûts de développements pour faire les sélections et modifications dans la table, seront bien plus importants : il faut gérer l'existence des téléphones avant de les modifier, gérer le cas de la suppression, ne pas se gourrer dans l'ordre, etc. => Dans ces trois cas, on alourdis considérablement les traitements de l'application cliente, qui risque de rapidement avoir des performances dégradées inutilement. C'est de ce genre d'optimisation qu'il est question lorsque je parle de dénormaliser pour des problèmes de performances. Ca, c'est des cas concrets, qui arrivent tous les jours à chacun de nous. Citation:
Je ne parle pas du tout de simplifier les requêtes SQL, on s'en cogne des requêtes SQL. Moi je parle de simplifier les algorythmes des applications consommatrices. Et ça, c'est une véritable réalité. Y'a qu'un chercheur de l'EDNAT pour croire qu'un modèle hyper normalisé est super optimisé pour une application de production : le SGBD, il ne faut pas oublier que c'est qu'un moyen de stocker les données. Si la représentation des données n'est pas compatible avec la représentation qu'on en a dans l'outils qui les traites, là il y a un vrai problème, bien plus grave qu'un souci de dénormalisation. Citation:
CODSOC et TYPTIE sont des données contextuelles, dont l'utilisateur n'a que faire. Donc si, SIGTIE est un identifiant unique pour un contexte donné (les clients du magasin, les fournisseur de la centrale d'achat, etc.) C'est donc une clé toute trouvée, dans le plus strict respect de la conceptualisation. |
|||
|
|
01
|
|
|
#53 | ||||||
![]() ![]() |
Citation:
C'est pourtant ainsi que fonctionnent toutes les applications correctement développées et interrogeant une BDD normalisée. Il ne me semble pas qu'on entende parler tous les matins de serveurs ou de réseaux qui plantent à cause de cette architecture ! Citation:
![]() Vous avez d'ailleurs pointé les conséquences de cette horreur ! Citation:
Citation:
No good ! Citation:
Citation:
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique. Autoentrepreneur. Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework... « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau) À la maison comme au bureau, j'utilise la suite Linux Mageïa ! |
||||||
|
20
|
|
|
#54 | |||
|
Membre Expert
![]() Sylvain DevidalChef de projets Générix Inscription : février 2010 Messages : 1 517 ![]() |
Citation:
Pourquoi alors dans les fermes de serveurs, on a des lignes 10base1000 dédiés entre chaque serveurs depuis 10 ans alors que sur les réseaux d'entreprise, on est encore la plupart du temps en 10base100 ? Pourtant les utilisateurs qui utilisent les applications consomment pas mal de ressource réseau... Mais rien comparé à ce que consommera un serveur de traintement. C'est justement pour cette raison qu'il est intéressant de limiter au maximum les échanges réseau entre le serveur de traitement et le serveur de base de données (donc essayer de faire le plus de choses possibles en une seule requête... et éviter de devoir faire des produits cartésiens inutiles). Citation:
L'ERP sur lequel je travaille utilise ce système pour construire dynamiquement des écrans : dans un fichier XML on indique qu'on veut parcourir un objet métier (qui correspond généralement à une table) et pour chaque ligne, on va parcourir d'autres objets métiers : on a donc rapidement des centaines (voir des milliers) de requête qui sont exécutées pour un seul écran. => Et si dans un nombre limité de cas, c'est une ineptie, la plupart du temps il serait "impossible" de faire tout en une seule requête. Citation:
|
|||
|
|
03
|
|
|
#55 |
|
Expert Confirmé
![]() |
A StringBuilder:
C'est un peu du tous contre vous là... Vous êtes donc le seul à détenir la vérité... Vous venez sur ce forum avec une pseudo (et unique?) expérience de la base de données (d'un ERP qui plus est...) en avançant perpétuellement des inepties (problèmes réseaux, effondrement de performance etc.) qui semblent sortir d'on ne sait où (enfin si de votre tête...) et surtout qui semblent être là pour vous auto-convaincre que le modèle que vous utilisez quotidiennement (qui n'est pas le votre qui plus est!) est le Saint-Graal de la modélisation. Les gens qui vous répondent ont pour la plupart une voir des dizaines d'années d'expériences derrière eux (je ne m'inclus pas dedans...), et autant d'années de participation et de publication sur ce forum... Je pense qu'ils ont une autre crédibilité que vous...
__________________
Prendre conscience, c'est transformer le voile qui recouvre la lumière en miroir. MCTS Database Development |
|
|
20
|
|
|
#56 |
|
Expert Confirmé Sénior
![]() ![]() ![]() François de Sainte MarieSpécialiste en bases de données Inscription : septembre 2006 Messages : 3 637 ![]() |
Bonsoir,
iberserk a bien résumé la situation. La communication avec StringBuilder est pratiquement impossible, car pour lui la modélisation des données doit être compatible avec la représentation qu’en fait l’outil (par référence à ce qu’il écrit, je ne pense pas me tromper : « Si la représentation des données n'est pas compatible avec la représentation qu'on en a dans l'outils qui les traites, là il y a un vrai problème »), ce qui revient à dire que la modélisation doit être faite en fonction de l’outil. Il y a là un viol manifeste de la règle d’indépendance de la modélisation des données : l’art de la modélisation exige que soit seulement pris en compte le « Quoi », l’ensemble des règles de gestion des données recensées dans le dossier de conception, indépendamment des outils et des traitements. Le « Comment » ne relève pas de la modélisation des données au niveau conceptuel. C’est au cours d’une étape ultérieure que l’on traite de l’adéquation du modèle et de la représentation qu’en a l’outil, par exemple en mettant en jeu le mécanisme des vues. Une précision concernant la [dé]normalisation : ce que nous appelons normalisation des bases de données (de la 2NF à la 6NF) est basé fondamentalement sur l’étude des dépendances fonctionnelles (et de leurs grandes soeurs) et joue un rôle crucial quant à la qualité et la robustesse du modèle, mais manifestement cela ne semble pas concerner StringBuilder qui ne jure que par un ERP où l’on « modélise » par exemple des entités-types « fourre-tout » (sic !) A son sens, dénormaliser (« comme un porc » bien entendu...) une table qui est en xNF ne consiste pas à se limiter à respecter seulement la (x-1)NF ou la (x-2)NF, etc., sinon il nous l’aurait fait savoir, mais vraisemblablement à « optimiser », ce qui n’a rien à voir (comme le rappelle Chris Date dans Logic and Databases, « Denormalization Considered Harmful », la confusion entre les termes « dénormalisation et « optimisation » est du reste très fréquente). Qui plus est, pour StringBuilder dénormaliser est du ressort des développeurs ! CinePhil aura beau faire des observations frappées au coin du bon sens, il s’agit désormais d’un dialogue de sourds. Mais comme cette discussion fait partie de la série « Débat » et peut servir à d’autres, de mon côté je m’arme de patience et vais prendre le relais...
__________________
_ Faites simple, mais pas plus simple ! (A. Einstein) E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire ») => La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale ») __________________ Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !) |
|
|
30
|
|
|
#57 | |||||
|
Expert Confirmé Sénior
![]() ![]() ![]() François de Sainte MarieSpécialiste en bases de données Inscription : septembre 2006 Messages : 3 637 ![]() |
Citation:
Autrement dit, violer la 2NF est radical pour rendre des tables obèses. Pour reprendre l’exemple auquel je fais référence, le poids de la table Membre non normalisée « pèse » en tout 36,85 Mo, alors qu’après normalisation le poids (tables Membre + Message) n’est plus que de 26,40 Mo. Supposons que dans le cas général des applications on ait en moyenne un téléphone par tiers. Puisque que les colonnes que vous affectez aux téléphones font partie de l’en-tête de la table TIERS des tiers, pour N tiers et Q octets par numéro de téléphone, il y a en gros une perte de l’ordre de 2/3 * N * Q octets. Si la base de données est celle des dix millions de clients d’une grande banque, et si toutes les tables sont structurées ainsi, on tend vers la gabegie. Aujourd’hui, un gigaoctets n’est rien, mais quand j’avais votre âge, les disques permettaient de stocker au mieux 30 Mo chacun (exemple IBM 2314) et ils coûtaient fort chers, on faisait donc attention (un de mes collègues avait réussi à n’avoir besoin que d’une vingtaine de Mo pour loger les données de 56 millions de bons anonymes...) Quoi qu’il en soit, comme disait Guillaume d’Ockham « Pluralitas non est ponenda sine necessitate », autrement dit, par souci d’économie il faut se défaire de ce qui n’est pas utile, ce qu’en l’occurrence on réalise en mettant en œuvre une table TEL des téléphones, quitte à prototyper les performances pour comparer. Que faire quand il faudra prendre en compte un 4e téléphone ? Puis un 5e ? Puis un nième ? Ajouter des colonnes à une table est lourd de conséquence, notamment sur la maintenance des développements. Que faire quand il faudra distinguer les types de téléphone (bureau, personnel, fax, ...) sans violer désormais la 3NF ? Votre exemple des téléphones illustre une approche horizontale (vectorielle) avec une cardinalité (degré, dimension) fixée, gravée dans le marbre. N’oubliez pas que les opérateurs relationnels sont orientés verticalité, avec cardinalité variable, notamment les opérateurs d’agrégation (SUM, COUNT, AVG, ...) Cela vous le savez puisque vous connaissez SQL. Citation:
Que le SGBD produise non pas X mais une seule ligne par tiers, c’est là un problème de mise en forme à prendre en compte au niveau de la requête, le but de la manœuvre étant de n’avoir qu’un seul échange entre le SGBD et l’application (conséquence d’une approche ensembliste) et non pas un échange pour chaque téléphone (conséquence d’un approche séquentielle). Si vous adaptez l’exemple proposé à drbs, vous aurez une vue, appelons-la TELV (MaVue dans l’exemple), permettant de représenter en une seule ligne les données contenues dans X lignes, et par jointure naturelle de la table TIERS avec la vue TELV, vous produirez le résultat attendu, avec pour conséquence que le nombre d’échanges entre le SGBD et l’application sera effectivement limité à un par tiers, donc sans que les performances du réseau soient pénalisées (au passage, voyez aussi les articles de SQLpro ayant trait par exemple aux CTE, aux procédures stockées etc.) Citation:
Citation:
En tout état de cause, comme je l’ai montré (notamment avec l’identification relative), nul besoin de chercher à optimiser en rapatriant les téléphones dans la table des tiers. Citation:
__________________
_ Faites simple, mais pas plus simple ! (A. Einstein) E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire ») => La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale ») __________________ Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !) |
|||||
|
|
40
|
|
|
#58 | ||||||||||
|
Expert Confirmé Sénior
![]() ![]() ![]() François de Sainte MarieSpécialiste en bases de données Inscription : septembre 2006 Messages : 3 637 ![]() |
Citation:
=> Sous-traiter le maximum de contrôles au SGBD (lequel est de plus en plus performant au fil des ans) et ne conserver dans les programmes que la partie productive. Citation:
Citation:
Citation:
Citation:
=> Pour deux tiers différents, la colonne SIGTIE peut avoir la même valeur, donc le singleton {SIGTIE} ne peut pas être clé candidate. Citation:
De la même façon, le matricule des vendeurs fait-il bien l’objet d’une colonne qui lui est propre (que ce soit dans la table TIERS ou une autre) ? Plus généralement, conformément aux règles de la modélisation, chaque donnée identifiante « naturelle » fait-elle bien l’objet d’une colonne qui lui est propre ? Citation:
Citation:
Citation:
Citation:
Effectivement, il faut changer d’outil, ou envoyer celui qui en est l’auteur apprendre à modéliser correctement (5NF, voire 6NF pour les données temporelles), apprendre l’algèbre relationnelle, apprendre à faire des vues. La modélisation des bases de données ne se fait pas en plaquant dans des tables ce qu’on voit à l’écran, la vue qu’on a de l’écran est indépendante (orthogonale comme dirait Chris Date) de la structure de la base de données.
__________________
_ Faites simple, mais pas plus simple ! (A. Einstein) E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire ») => La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale ») __________________ Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !) |
||||||||||
|
|
30
|
|
|
#59 |
|
Membre Expert
![]() Sylvain DevidalChef de projets Générix Inscription : février 2010 Messages : 1 517 ![]() |
Merci pour ces longues réponses très intéressantes.
Elles le seraient plus si je n'étais pas déjà convaincu de ce que vous dites. Le seul problème, et effectivement, pour cette raison, j'estime le débat clos, c'est qu'on ne parle absolument pas de la même chose, et au lieu de chercher un terrain d'entente pour comprendre ce que l'autre a à dire, on ne fait qu'énnoncer des expériences diverses, illustrées par des cas plus théoriques. Tout ce que je remarque ici, c'est qu'il est question d'un topic sur "quand" dénormaliser. Si la question se pose, c'est qu'il doit vraisemblablement y avoir des cas où effectivement, cela peut devenir judicieux, voir nécessaire. Ou alors ce topic est inutile, ou tout du moins, mal nommé. Depuis le début, les différents intervenants s'égosillent à dire qu'il fait absolument modéliser dans les règles de l'art, et qu'il ne faut surtout pas dénormaliser. Il se trouve que je travaille sur un outils (peut-être visionnaire, peut-être complètement à côté de la plaque) qui est parti d'un raisonnement simple : - Un SGBD, c'est un outils informatique, qui va être utilisé par un autre outils informatique - En informatique, il existe des notions d'héritage, de surcharge - Pourquoi ne pas appliquer ces principes au modèle des données Ceci a pour effet, avec cet outils (mais aussi d'autres, avec lesquels j'ai déjà eu l'occasion de travailler) de mutualiser un certain nombre d'entités, afin de stocker, avec la même structure, les mêmes algorithmes, des données de nature différentes. L'intérêt ? Il est pourtant simple : alors que vous me repprochez de devoir ajouter une colonne lors d'une montée en version, vous êtes absolument pas étonnés de devoir rajouter des tables à tout bout de champs, dès que le besoin s'élargit... alors que la solution que je donne comme exemple, permet de conserver le même modèle des données. Il y a tout de même une certaine contradiction vous ne trouvez pas ? Le fait de stocker en effet dans une unique table "tie" des informations telles que des clients, fournisseurs, dépôts, magasins, etc. demande malheureusement de faire quelques concessions en ce qui concerne la dénormalisation : colonnes vides notamment. Cependant, vu qu'il s'agit d'un outil d'éditeur (ERP ou non, j'insiste sur le fait qu'il s'agit d'un outils d'éditeur) il existera toujours des éléments dans le modèle de données qui ne seront pas exploités, qu'il soit hyper normalisé ou non. En revanche, le même outil peut servir à Générale de Santé à gérer la pose de prothèses sur des patients ainsi que leurs prescriptions médicales, ou à La Dauphinoise l'ensemmancement des champs de ses clients, ou à CDiscount la gestion de ses ventes en ligne. Le tout avec exactement la même base de données, alors que ni les procédures métier, ni les informations nécessaires au fonctionnement de ces sociétés n'ont quoi que ce soit en commun. Ce modèle n'est pas parfait, certains choix de dénormalisation peuvent être reprochés, mais je n'admettrai jamais qu'on puisse remettre en question l'aspect praticité, fiabilité et performance des choix qui ont été faits. J'insiste donc sur ce point. Il ne faut pas confondre la modélisation "pointue", où chaque élément du dictionnaire de données est maîtrisé, chaque lien entre information est scellé dans le marbre, avec la modélisation "générique", qui permet au plus grand nombre de profiter du même outils : ça n'a rien à voir. Dans le premier cas, oui, normaliser le plus possible est tout à fait normal : c'est la garantie de pouvoir maîtriser le modèle des données et son évolution. Dans le second cas, en l'abscence d'un dictionnaire des données, en l'absence d'une large majorité des règles de gestion, il est évident que la modélisation ne sera pas du tout pensée de la même façon. Un exemple simple : - Lorsque CDiscount vend des produits hifi, certaines propriétés sont parfaitement dédiées au fait que le produit soit hifi. Ce nombre de propriétés est illimité, et diffère d'un produit à l'autre (y'a pas d'auteur sur un lecteur de DVD de salon, et y'a pas de classe d'énergie sur un CD Audio). => Le modèle des données doit déjà pouvoir s'adapter à ça. C'est pas simple quand on est en mode "normalisation pure". Personnellement, à froid, je n'ai pas de solution toute prête pour ça (démultiplication des tables de produit en fonction des types de produits ? démultiplication des tables de propriétés ? quelle solution n'est pas usine à gaz lors de l'allimentation/interrogation ?) - Mais maintenant, quand Générale de Santé arrive, et commence à parler de prothèses des hanches, de pathologies clients... ça commence à faire beaucoup de différences ! => Avec un choix de modélisation "classique", à part se retrouver avec des dizaines, centaines de table pour gérer bêtement des propriétés de produits, je ne vois pas trop comment faire. Le principe de cet outils dont je parle, c'est justement de conserver le même modèle des données pour tout le monde. Alors toutes ces propriétés, c'est géré en 3 tables, avec beaucoup moins de null que vous n'imaginez : il ne s'agit pas de créer des tables "fourre-tout" avec 500 colonnes "au cas où un jour ma prothèse mamaire n'est un tuner FM", mais d'une table "fourre-tout" avec un faible nombre de colonnes, mais une ligne par propriété, et une description des contraintes dans une troisième table. C'est une façon complètement différente de penser. D'un point de vue performances, j'accepte sans condition que ce type de modélisation peut avoir quelques lacunes par rapport à de la modélisation normalisée. Cependant, bien moins inportante que vous ne voulez bien le dire, sinon je me demande comment nos clients feraient pour fonctionner vu leur volumétrie et la tronche de leurs serveurs (chez Animalis ? Ah, ben c'est un vieux Pentium II qui fait tourner la base... c'est pas hyper rapide, mais ça tourne bien... j'ai jamais vu de souris blanche dans un aquarium dans leurs rayons). L'optimisation, elle ne se trouve pas seulement au niveau "matériel" du serveur de base de données, ni même d'application. L'optimisation, elle se trouve aussi au niveau de la maintenance, et de la mise en oeuvre des projets, des montées en version de l'outil (qu'est-ce que le prix d'un serveur plus gros en contrepartie de milliers d'heures de mise en oeuvre ?) Voilà. J'accepte sans aucun problème qu'une bonne modélisation est une modélisation sans (ou presque) dénormalisation. Mais acceptez quand même qu'il existe des cas où OUI, la dénormalisation est nécessaire. C'est tout ce que je vous demande. |
|
|
00
|
|
|
#60 | |
|
Expert Confirmé
![]() |
Citation:
Le modèle pour les articles est le modèle dit par méta données (une table des type de caractéristique d'article, une table article, une table d'association article/caractéristique -> valeur). SQLPRO a d'ailleurs fait un excellent article à ce sujet: http://sqlpro.developpez.com/cours/m...n/metadonnees/ Et j'utilise d'ailleurs cette modélisation dans le cadre de mon travail (site ebusiness clé en main connecté à différents type d'ERP). Par ailleurs CDISCOUNT est un mauvais exemple, leur "modélisation" une catastrophe et leurs problèmes à ce sujet important... (les caractéristiques sont stockées en XML dans une colonne de l'article)
__________________
Prendre conscience, c'est transformer le voile qui recouvre la lumière en miroir. MCTS Database Development |
|
|
|
30
|
Copyright © 2000-2013 - www.developpez.com