IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Schéma Discussion :

Quelles sont les règles d'or pour concevoir un bon MCD ? [MCD]


Sujet :

Schéma

  1. #1
    Membre averti
    Avatar de wafiwafi
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    500
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 500
    Points : 328
    Points
    328
    Par défaut Quelles sont les règles d'or pour concevoir un bon MCD ?
    Une question m'a été posée et j'aimerais avoir votre avis sur la réponse que j'ai fournie.
    Quelles sont les règles d'or pour concevoir un bon MCD ?
    Ma réponse : on ne peut parler d'un bon MCD que si on évoque les applications qui vont l'accompagner et l'étude de la complexité du programme pour minimiser les lectures et écritures sur disques sans parler du problème de la recherche des objets dans le cas d'une grande quantité d'informations dans les bases...
    Pour moi, une règle de base à respecter est de bien s'assurer que le modèle construit de façon bien réfléchie, répond au cahier des charges fixé au départ. Parler de règle d'or au stade d'un MCD me parait prématuré ; en résumé, on ne peut juger un bon MCD qu'à partir de ce que vous voulez en faire par la suite.
    En essayant de réduire les classes au maximum par exemple, cela voudrait il dire qu'on a amélioré le MCD ? Pour moi, ce n'est pas forcement le cas. Certaines personnes le font de façon automatique mais non réfléchie.
    Des règles à respecter existent, elles ne sont pas d'or mais imposées pour respecter la construction.
    À travers vos diverses expériences, vous pouvez agir. Qui sait peut être des règles d'or existent réellement à ce stade.
      1  0

  2. #2
    Membre chevronné
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Août 2007
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projet en SSII

    Informations forums :
    Inscription : Août 2007
    Messages : 797
    Points : 2 065
    Points
    2 065
    Par défaut
    Bonjour,

    Citation Envoyé par wafiwafi Voir le message
    Ma réponse : On ne peut parler d'un bon MCD que si on évoque les applications qui vont l'accompagner et l'étude de la complexité du programme pour minimiser les lectures et écritures sur disques sans parler du problème de la recherche des objets dans le cas d'une grande quantité d'informations dans les bases...
    Le MCD (Modèle Conceptuel de Données) a pour objet la représentation (l'abstraction) d'une réalité. Son invariant est le métier : le "QUOI".

    Par conséquent le reste est variable : organisations, fonctions, technologies, etc. Ce qui signifie que le MCD doit être indépendant de ces variables : il ne doit pas tenir compte de l'organisation de l'entreprise, ni des applications qui vont utiliser la ou les bases de données résultantes, encore moins des technologies d'implémentation.

    Donc, s'il devait y avoir une règle d'or (ou règle de base ou règle essentielle) pour la modélisation conceptuelle, ce serait, à mon avis : Un bon MCD est celui qui modélise correctement 100% des règles de gestion du cahier des charges (ce qui transparait dans la citation de wafiwafi ci-dessous).

    Citation Envoyé par wafiwafi Voir le message
    Pour moi, une règle de base à respecter est de bien s'assurer que le modèle construit de façon bien réfléchie, répond au cahier des charges fixé au départ.
      4  0

  3. #3
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 128
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 128
    Points : 31 677
    Points
    31 677
    Billets dans le blog
    16
    Par défaut Indépendance des tâches à mener
    Bonjour,


    Citation Envoyé par wafiwafi Voir le message
    On ne peut parler d'un bon MCD que si on évoque les applications qui vont l'accompagner et l'étude de la complexité du programme pour minimiser les lectures et écritures sur disques sans parler du problème de la recherche des objets dans le cas d'une grande quantité d'informations dans les bases...
    Pour confirmer ce que dit JPHi33, on ne traite pas des accès au disque quand on est sur la dunette (c'est-à-dire au niveau conceptuel) : « minimiser les lectures et écritures sur disques » fait l’objet d’une étude distincte de la modélisation conceptuelle des données. Descendons dans la soute, pour que vous compreniez bien pourquoi.

    Cette étude suppose en effet que l’on ait connaissance du SGBD qui sera utilisé. Elle pourra être appelée « prototypage des performances » et devra être menée par un spécialiste, un DBA (administrateur des bases de données) qui construira son prototype suite aux entretiens qu’il aura eus avec le chef de projets, afin d’identifier les traitements les plus sensibles (disons le top 10). Dans ce genre d’exercice, il ne s’agit pas de programmer, mais de façon pragmatique — à partir d’un jeu de tables ayant à peu de choses près la structure de celles qui seront dérivées du MCD, avec une volumétrie suffisante pour les mesures —, construire un ensemble de requêtes (disons SQL) qui ne sont jamais que des brouillons, mais qui suffisent pour connaître la performance globale, bien avant que ne démarrent les véritables développements, lesquels tiendront évidemment compte du verdict du prototype.

    Maintenant, il est vrai que les résultats fournis par le prototype, peuvent avoir quelques retombées sur le MCD lui-même, telles que l’utilisation de l’identification relative — plutôt qu’absolue —, qui normalement est une conséquence de la sémantique des données (par exemple, une ligne de facture est à identifier relativement à une facture). Mais normalement, cela ne remet pas en cause le MCD lui-même (ou alors c’est que le concepteur n’a pas fait vraiment son travail).

    De même que lorsque vous bâtissez un MCD vous ne vous focalisez pas sur les accès au disque, de même vous ne vous mêlez pas d’autres problèmes sensibles qui ne sont pas de votre ressort et auxquels vous n’aurez pas songé (heureusement, sinon vous ne dormiriez pas), par exemple : les étreintes fatales et autres verrous mortels, dus à une trop forte concurrence des utilisateurs lors des opérations de mises à jour. Là encore, c’est le prototype qui devra permettre de mettre en évidence ces phénomènes, d’anticiper quant à l’organisation de la structure physique des tables afin d’éliminer ce genre de problèmes.

    Ou encore, vous ne vous préoccupez pas des droits des utilisateurs sur les données, sauf à élaborer un MCD annexe à cet effet. Là encore, chaque chose en son temps, comme dit l’autre « the right man in the right place, at the right time ».

    Ce qui signifie encore : indé-pen-dan-ce.

    Bref, pendant que le DBA construit son prototype, assurez-vous déjà auprès du chef de projets que le cahier des charges est à peu près complet, sinon vous-même, le chef de projets — et finalement tout le monde, y compris la Maîtrise d’ouvrage — connaîtriez bien de bien cruelles désillusions.
      2  0

  4. #4
    Rédacteur
    Avatar de benwit
    Profil pro
    dev
    Inscrit en
    Septembre 2004
    Messages
    1 676
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : dev

    Informations forums :
    Inscription : Septembre 2004
    Messages : 1 676
    Points : 4 265
    Points
    4 265
    Par défaut
    Citation Envoyé par fsmrel Voir le message
    Maintenant, il est vrai que les résultats fournis par le prototype, peuvent avoir quelques retombées sur le MCD lui-même, telles que l’utilisation de l’identification relative — plutôt qu’absolue —, qui normalement est une conséquence de la sémantique des données (par exemple, une ligne de facture est à identifier relativement à une facture).
    Ok pour la sémantique.
    Mais la clé relative nécessite une clé composé n'est-ce pas ?

    Perso, j'ajoute systématiquement une clé "technique" qui est ma clé primaire. Cela évite à la fois le passage des x attributs composants la clé fonctionnelle et les lourdeurs lors d'évolution du schéma.
    Cela n'empêche pas que les attributs de la clé "fonctionnelle" soient présents et de poser une contrainte d'unicité dessus.
      1  0

  5. #5
    Expert éminent sénior
    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 812
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 812
    Points : 34 084
    Points
    34 084
    Billets dans le blog
    14
    Par défaut
    Effectivement, l'identification relative entraîne en principe une clé composée.

    Exemple pour une chaîne d'hôtels et leurs chambres. Dans chaque hôtel, les chambres sont numérotées de 1 à n.

    Il y a deux possibilités :
    1) La "normalisée" :
    Chambre -(1,1)----Situer----1,n- Hôtel

    Hotel (H_Id, H_Nom...)
    Chambre (Ch_IdHotel, Ch_Numero, ...)

    Identification relative de la chambre par rapport à son hôtel, la chambre est identifiée par la clé composée (Ch_IdHotel, Ch_Numero).

    2) La vôtre :
    Chambre -1,1----Situer----1,n- Hôtel

    Hotel (H_Id, H_Nom...)
    Chambre (Ch_Id, Ch_IdHotel, Ch_Numero, ...)

    Identifiant artificiel pour la chambre et obligation de mettre une contrainte UNIQUE sur le couple (Ch_IdHotel, Ch_Numero).

    Dans un cas comme celui-ci où les chambres sont numérotées par des entiers, je préfère la solution normalisée.

    Si par contre les hôtels ont donné un joli nom à leurs chambres à la place de numéros, je préfère l'identifiant artificiel car une colonne de type textuel dans une clé primaire, je n'aime pas car le nom de la chambre peut changer et bonjour les mises à jour dans toutes les tables dérivées, le modèle n'est alors pas solide.
      1  0

  6. #6
    Rédacteur
    Avatar de benwit
    Profil pro
    dev
    Inscrit en
    Septembre 2004
    Messages
    1 676
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : dev

    Informations forums :
    Inscription : Septembre 2004
    Messages : 1 676
    Points : 4 265
    Points
    4 265
    Par défaut
    Si on pouvait se passer de données "superflues" (id artificiel), ça serait mieux en effet.

    En prenant votre exemple, ma solution me met à l'abri de certaines difficultés trop souvent rencontrées !

    Par analogie, dans votre exemple, cela donne :

    Au début du projet, les chambres sont identifiés comme vous le dites par un numéro. On choisit donc la solution de la clé composée.

    Puis avec l'évolution, il y a :
    - une table de jointure où la chambre apparaît (2 attributs) avec une autre clé composé (3 attributs) et une troisième (3 attributs). Pour ce triplet (si id technique), on doit se trimballer du coup avec 8 attributs !
    (je ne parle même pas d'une table ou la clé composé avait 7 attributs !)

    - la nouvelle directrice de l'hotel décide de renuméroter les chambres en tenant compte de l'étage et ... du coté pair/impaire du couloir !
      0  0

  7. #7
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 128
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 128
    Points : 31 677
    Points
    31 677
    Billets dans le blog
    16
    Par défaut
    Citation Envoyé par benwit Voir le message
    la clé relative nécessite une clé composé n'est-ce pas ?
    Je suppose que par clé relative vous entendez l’homologue au niveau logique de l’identifiant relatif du modèle conceptuel de données (MCD). Toujours au niveau logique (disons SQL), prenons l’exemple du couple Commande (table Commande), Ligne de commande (table LigneCommande). Si la table Commande a pour clé primaire {CommandeId}, la table LigneCommande aura pour clé primaire {CommandeId, LigneCommandeIdRel}, LigneCommandeIdRel étant un séquenceur dont l’objet est de distinguer chaque ligne de commande d’une commande donnée et dont la valeur est égale à 1 pour la 1re ligne de commande de chaque commande, égale à 2 pour la 2e ligne, etc. (Il va de soi que la valorisation de chaque attribut entrant dans la composition d’une clé primaire est du ressort du système et en aucune façon l’utilisateur n’a son mot à dire : on lui laisse les propriétés naturelles).

    La clé de la table LigneCommande n’est pas un singleton, mais une paire : c’est une « clé composée » pour reprendre votre expression. Y verriez-vous un quelconque inconvénient ?


    Citation Envoyé par benwit Voir le message
    j'ajoute systématiquement une clé "technique" qui est ma clé primaire
    Reprenons l’exemple des deux tables Commande et LigneCommande et suivons votre recommandation : utilisons une clé primaire singleton {LigneCommandeIdAbs} pour la table LigneCommande. Si vous relisez avec attention ce que j’ai écrit dans mon message précédent, l’objet du prototype est, entre autres, de vérifier que la performance concernant les accès à cette table est acceptable. En fonction de son verdict, on pourra être amené à remplacer la clé primaire singleton {LigneCommandeIdAbs} par la paire {CommandeId, LigneCommandeIdRel}. Pour ma part, à chaque fois que j’ai prototypé, j’ai constaté que dans ce genre de scénario (relation entre une entité-type et ses propriétés multivaluées) c’était un bienfait pour les accès (réduction du nombre de pages à charger en mémoire donc des temps d'attente de fin de chargement). En conséquence, je suis amené à utiliser l’identification relative au niveau du MCD.


    Citation Envoyé par benwit Voir le message
    Cela évite à la fois le passage des x attributs composants la clé fonctionnelle et les lourdeurs lors d'évolution du schéma.
    Je suppose que par « passage des x attributs », vous voulez dire qu’il y a de la redondance et que cela coûte cher en occupation mémoire ? En réalité, cette redondance est utilisée pour les opérations de jointure et elle est complètement contrôlée par le SGBDR (à condition évidemment de mettre en œuvre les contraintes d’intégrité référentielle qui sont les conséquences au niveau logique SQL des associations-types du MCD). Concernant l’occupation mémoire, un attribut tel que LigneCommandeIdRel coûte deux octets, contre quatre octets pour l’attribut LigneCommandeIdAbs (c'est-à-dire quand la table LigneCommande doit pouvoir contenir plus de 32767 lignes de commande).

    Quant « aux lourdeurs lors d'évolution du schéma » (conceptuel ? logique ?) je ne vois rien qui puisse confirmer votre affirmation. Merci de fournir un exemple.
      1  0

  8. #8
    Membre expert
    Homme Profil pro
    Retraité
    Inscrit en
    Octobre 2005
    Messages
    1 473
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 66
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Retraité
    Secteur : Finance

    Informations forums :
    Inscription : Octobre 2005
    Messages : 1 473
    Points : 3 289
    Points
    3 289
    Par défaut
    Citation Envoyé par benwit Voir le message
    ...
    Perso, j'ajoute systématiquement une clé "technique" qui est ma clé primaire. Cela évite à la fois le passage des x attributs composants la clé fonctionnelle et les lourdeurs lors d'évolution du schéma.
    Cela n'empêche pas que les attributs de la clé "fonctionnelle" soient présents et de poser une contrainte d'unicité dessus.
    Et on a déjà "claqué" deux index au lieu d'un seul ... ça va pas accélérer les insertions dans la table tout ça ...
      1  0

  9. #9
    Rédacteur
    Avatar de benwit
    Profil pro
    dev
    Inscrit en
    Septembre 2004
    Messages
    1 676
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : dev

    Informations forums :
    Inscription : Septembre 2004
    Messages : 1 676
    Points : 4 265
    Points
    4 265
    Par défaut
    Citation Envoyé par fsmrel Voir le message
    Reprenons l’exemple des deux tables Commande et LigneCommande et suivons votre recommandation : utilisons une clé primaire singleton {LigneCommandeIdAbs} pour la table LigneCommande. Si vous relisez avec attention ce que j’ai écrit dans mon message précédent, l’objet du prototype est, entre autres, de vérifier que la performance concernant les accès à cette table est acceptable. En fonction de son verdict, on pourra être amené à remplacer la clé primaire singleton {LigneCommandeIdAbs} par la paire {CommandeId, LigneCommandeIdRel}. Pour ma part, à chaque fois que j’ai prototypé, j’ai constaté que dans ce genre de scénario (relation entre une entité-type et ses propriétés multivaluées) c’était un bienfait pour les accès (réduction du nombre de pages à charger en mémoire donc des temps d'attente de fin de chargement). En conséquence, je suis amené à utiliser l’identification relative au niveau du MCD.
    Si je vous comprend bien, utiliser la paire {CommandeId, LigneCommandeIdRel} est un bienfait par rapport au singleton {LigneCommandeIdAbs} ?
    Je suis un peu étonné.
    Si je conçois bien que l'utilisation du singleton {LigneCommandeIdAbs} est plus coûteux en espace (et donc en temps par rapport à ce que vous avez dit), j'avais initialement pensé que si le sgbd n'avait à travailler que sur un attribut unique (pour la clé) au lieu d'un n-uplets d'attributs entrant dans la composition de la clé, ça serait "moins couteux" en temps ?
    J'avoue que c'était un a priori étayé par aucune compétence en ce domaine.
    Si vous me confirmez que l'utilisation des identifiants relatifs est "meilleur" pour les perfs (suivant votre expérience bien entendu car on peut certainement trouver des exceptions et c'est le prototypage qui tranchera), c'est tant mieux car je trouve votre solution plus propre que l'ajout superflu d'une clé technique.


    Citation Envoyé par fsmrel Voir le message
    Quant « aux lourdeurs lors d'évolution du schéma » (conceptuel ? logique ?) je ne vois rien qui puisse confirmer votre affirmation. Merci de fournir un exemple.
    En préférant l'ajout d'une clé singleton purement "technique", je ne pensai pas particulièrement aux performances (d'ailleurs, mon "à priori" semble se révéler faux) mais plutôt à un aspect pratique de développeur (utilisateur de sgbd ayant donc un point de vue moins technique que le vôtre)

    Je vais essayé de trouver des arguments :

    La simplicité des requêtes :

    J'ai toujours trouvé plus simple de faire des requêtes avec ou sans jointures lorsque la clé d'une table n'est pas composée. Je reconnais que c'est du détail mais quand il faut se trimballer pour une simple recherche, une clé composé de 7 attributs (mappé avec leur équivalent objet), c'est un peu pénible.

    Une clé technique unique moins contraignante pour l'évolution du schéma ?

    En revanche, la clé technique singleton me semble meilleur vis à vis de l'évolution du schéma. Je parle d'une évolution conceptuelle ayant forcément une conséquence sur le modèle logique.

    A priori, oui ...
    La clé singleton ayant été introduite pour des raisons purement technique (et je ne parle pas de perf), elle est donc indépendante de l'évolution fonctionnelle. J'ai le sentiment qu'une clé métier est plus "contraignante" (composée de plusieurs attributs avec des contraintes) pour moi développeur qu'une unique clé technique.

    ... mais à la réflexion non à iso-cohérence ...
    J'avoue que j'avais initialement pensé que le contrôle de l'unicité de ma clé technique était plus "rapide" que le contrôle de l'unicité d'une clé fonctionnelle composée d'un n-uplet d'attributs.
    Je me rend compte du biais introduit car si pour être égaux face au problème d'intégrité, je doit ajouter une contrainte d'unicité sur ces mêmes attributs (ceux entrant dans votre clé fonctionnelle), je ne dois plus rien "gagné".

    ... mais peut être un peu mieux pour les tables de jointures ?
    Supposons deux entités A et B avec une relation n/n et donc une table de jointure TJ_AB dans le modèle logique. Si une des entités "évolue" et nécessite des attributs supplémentaires dans TA, avec la clé fonctionnelle, je devrai également modifier ma table TJ_AB alors puisque tous les attributs entrant dans la composition de la clé TA devront aller dans TJ_AB.
    La clé unique technique nous met à l'abri de cela et si on a beaucoup de relations, ça peut quand même simplifier les modifications nécessaires non ?

    Conclusion :
    Ceci dit et c'est ce qui a de bon dans ces discussions, en rédigeant mon point de vue, je me rend compte qu'il est discutable.
    • Côté performance, pas mieux (et d'après Luc Orient, même pire avec un index supplémentaire ).
    • Côté facilité développement, du détail et sûrement de la gnognotte pour un dba.
    • Côté évolution du schéma, même impact pour la table concernée (l'impact est identique à cohérence des données identiques) et un impact moindre sur les tables de relations.


    J'espère avoir été plus clair et j'attends vos remarques.

    Question aux DBA : l'utilisation d'une clé technique est donc un anti pattern ?
      0  0

  10. #10
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 128
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 128
    Points : 31 677
    Points
    31 677
    Billets dans le blog
    16
    Par défaut De l'identification relative à l'I/O bound
    Bonsoir,


    Citation Envoyé par Luc Orient Voir le message
    Citation Envoyé par benwit Voir le message
    Perso, j'ajoute systématiquement une clé "technique" qui est ma clé primaire. Cela évite à la fois le passage des x attributs composants la clé fonctionnelle et les lourdeurs lors d'évolution du schéma.
    Cela n'empêche pas que les attributs de la clé "fonctionnelle" soient présents et de poser une contrainte d'unicité dessus.
    Et on a déjà "claqué" deux index au lieu d'un seul ... ça va pas accélérer les insertions dans la table tout ça ...
    Il est vrai que la mise en oeuvre d’un index supplémentaire n’est pas fait pour améliorer la performance des mises à jour. Cela dit, deux index pour une table ça n’est pas dramatique (en tant que DBA DB2, vous comme moi en avons vu d’autres...) Ça pourrait devenir vraiment gênant, si l’on montait à cinq index et plus pour une table mise fortement à contribution en mise à jour. Pour reprendre mon exemple des commandes (table Commande) et lignes de commande (table LigneCommande), Je devrai définir au moins deux index pour la table Commande :
    — Un pour la clé primaire {CommandeId},
    — Un pour la clé « utilisateur », à savoir le numéro de commande (appelons-le CommandeNo}.
    En effet, l’attribut CommandeId n’est pas visible par l’utilisateur, il est invariant, sans signification, ça n’est qu’un vulgaire séquenceur (ou un résultat de hachage) dont les valeurs sont fournies par le système. Il correspond à la « clé technique » dont parle benwit et sera utilisé pour les opérations de jointure, pour l’intégrité référentielle et autres joyeusetés dont l’utilisateur n’a pas conscience. En revanche, celui-ci pourra faire ce qu’il veut du numéro de commande, le structurer à sa façon, ça n’est jamais que le point d’entrée dont il a besoin et ce numéro de commande n'apparaît qu'une fois dans la base de données.

    Pour en revenir au niveau conceptuel, cela veut dire que l’identifiant de l’entité-type Commande sera représenté par {CommandeId}, tandis que CommandeNo fera l’objet d’un identifiant alternatif {CommandeNo}.

    Je redescends dans la soute. Reste le cas du troisième index (jamais deux sans trois ?) En effet, chaque commande est en relation forte avec le client. Intervient donc maintenant le troisième larron, le client, symbolisé par l’entité-type Client (ayant pour identifiant {ClientId}, invariant lui aussi, sans signification, inodore et sans saveur). A son tour la table Client aura pour clé primaire {ClientId}, ce qui conduit inexorablement à retrouver ClientId comme attribut de la table Commande et servant à effectuer la jointure des tables Client et Commande, garantir l’intégrité référentielle, etc. Il est sûr que sans index, l’opération consistant à retrouver les commandes d’un client ne sera pas performant...

    Je m’arrête un moment pour traiter de l’organisation de l’index « primaire » la table Client. Cet index a pour clé {ClientId}. Supposons que l’on soit dans le contexte d’une saisie de commandes intense. Si le prototype montre que plus les utilisateurs entrent en concurrence (disons aux heures de pointe, après la pause café-cigarette), plus les phénomènes de contention se manifestent, alors il faudra hacher ClientId plutôt que le valoriser séquentiellement. Par exemple, si pour le dix-millième client, la procédure de hachage fournit la valeur '5246901' pour la clé {ClientId} et la valeur '314' pour le client suivant, il est très probable que ces clients se retrouveront dans des pages (enregistrements) physiques éloignées (table space et index), d’où élimination des contentions. J’ai eu à traiter ce genre de problème pour un de mes clients, qui par la suite a commercialisé son application : très logiquement, plus la base de données de ses propres clients était réduite en volume et plus les problèmes de contention refaisaient surface...

    Revenons-en aux commandes. Même si l’on ne peut pas se permettre de supprimer un client qui a des commandes et/ou des factures en cours, sémantiquement parlant, les commandes sont viscéralement attachées au client (voir l’agrégation au sens UML), et donc pourquoi ne pas identifier l’entité-type Commande relativement à l’entité-type Client ? Aux niveaux logique et physique, on y trouve son intérêt. D’accord, la clé primaire de la table Commande n’est plus le singleton {CommandeId}, mais la paire {ClientId, CommandeId} : les valeurs que prend l’attribut CommandeId ne sont plus absolues, elles deviennent relatives à l’attribut ClientId, On observera surtout que le troisième index devient inutile, l’index « primaire » étant utilisé à la fois pour la clé primaire et pour la clé étrangère.

    Par ailleurs, les observations faites au sujet des phénomènes de contention concernant la table Client valent pour les commandes, puisque la saisie des commandes des clients '5246901' et '314' auront là aussi très peu de chance de provoquer des collisions. En revanche, les commandes d’un même client seront regroupées dans la même page physique (qu’il s’agisse du table space, ou de l’index), car on aura pris soin de définir l’indes primaire comme cluster (au sens DB2 for z/OS du terme). Désolé pour ces considérations techniques, mais c’est l’administration de la base de données et sa performance qui est en jeu. Et je pense qu’un DBA SQL Server s’y retrouvera dans ce jargon. Concrètement, cela veut quand même dire qu’en moyenne, en un seul accès au disque, on fera « monter » en mémoire toutes les commandes d’un client donné, alors que si l’on conserve la clé primaire singleton {CommandeId}, non seulement il faut « claquer » le troisième index, mais en plus les commandes d’un client sont éparpillées sur le disque (en général on ne les saisit pas en même temps) et il faut plusieurs accès au disque pour les récupérer, au lieu d’un seul. Et si un accès coûte une dizaine de millisecondes (merci à Luc Orient de donner l’ordre de grandeur qu’il constate chez lui...), on peut se mettre à ramer désespérément. Au prototype de montrer que l’on est CPU bound (limité par la puissance du processeur) ou bien I/O bound (le processeur hyper puissant est au chômage technique, on se tourne les pouces et on s’énerve parce qu’il faut attendre la fin des entrées/sorties disque).


    Citation Envoyé par benwit Voir le message
    Si je vous comprend bien, utiliser la paire {CommandeId, LigneCommandeIdRel} est un bienfait par rapport au singleton {LigneCommandeIdAbs} ?
    Je suis un peu étonné.
    Paradoxalement, conséquence logique de ce qui précède, le triplet {ClientId, CommandeId, LigneCommandeIdRel} est encore meilleur. Cette fois-ci, les lignes de commande constituent vraiment une propriété multivaluée d’une commande et, par exemple, en cas de suppression de la commande, doivent disparaître avec celle-ci (les UMLiens parleraient alors de composition), au même titre que la date de la commande (même si cette date n’est qu’un attribut monovalué). Au niveau physique, là encore l’index « primaire » sert aussi pour la clé étrangère en relation avec la commande, l’effet cluster jour encore plein pot et l’on évite les phénomènes d’I/O bound, pour le plus grand bien de la performance des requêtes.

    Allons plus loin. Supposons que le fournisseur s’engage à respecter une commande non pas en bloc, mais en fonction des quantités à livrer et des disponibilités des articles : ceux que l’on a en stock, ceux qui sont en cours de livraison, en cours de fabrication, etc.

    A son tour, la ligne de commande va comporter une propriété multivaluée « Engagement su ligne de commande » (appelons-la Engagement). Si l’on poursuit la modélisation conceptuelle dans le même esprit, à son tour l’entité-type Engagement sera identifiée relativement à l’entité-type LigneCommande. Au niveau logique, la table Engagement aura pour clé primaire {ClientId, CommandeId, LigneCommandeIdRel, EngagementId}.

    Mais on ne va pas en rester là, car il va falloir livrer tout ça... Or, si l’on s’est engagé pour telle quantité de tel produit et qu’il faut plusieurs camions pour livrer, il faudra poursuivre le jeu des poupées russes et définir une entité-type Livraison, identifiée relativement à l’entité-type Engagement. Au niveau logique, la table Livraison aura pour clé primaire {ClientId, CommandeId, LigneCommandeIdRel, EngagementId, LivraisonId}. (Pour la petite histoire, cette clé occupe physiquement 12 octets en tout (4 octets pour l’attribut ClientId + 2 octets pour chacun des autres attributs).

    J’ai noté que vous n’aimiez pas trop les clés multi-attributs, notamment parce que les jointures vous paraissaient bien lourdes, je cite :
    « quand il faut se trimballer pour une simple recherche, une clé composé de 7 attributs »
    Avec la clé primaire de la table Livraison, on n’en est pas loin... Mais un DBA se fera un plaisir de créer les vues occultant tout ça.

    Cela dit, si vous voulez connaître les camions qui sont prévus pour les livraisons en attente concernant le client Tartempion, vous n’êtes pas obligé de faire mention dans la requête SQL de l’ensemble des tables que l’on vient de voir. Il suffira de procéder à la jointure des deux seules tables Client et Livraison (plus la table Camion).

    Question : pour cette même requête, combien de tables seront à joindre, à « trimballer » sans le mécanisme de l’identification relative ?



    Citation Envoyé par benwit Voir le message
    Question aux DBA : l'utilisation d'une clé technique est donc un anti pattern ?
    Pourriez-vous préciser votre pensée ?
      1  0

  11. #11
    Rédacteur
    Avatar de benwit
    Profil pro
    dev
    Inscrit en
    Septembre 2004
    Messages
    1 676
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : dev

    Informations forums :
    Inscription : Septembre 2004
    Messages : 1 676
    Points : 4 265
    Points
    4 265
    Par défaut
    Mais par vos précieuses et limpides explications.
    Encore une fois (par rapport à d'autres discussions), je me rend compte qu'on ne peut pas s'abstraire totalement du côté technique. Comprendre comment ça fonctionne est encore nécessaire, n'en déplaise à certains

    Citation Envoyé par fsmrel Voir le message
    Pourriez-vous préciser votre pensée ?
    En fait, je pensai initialement que la clé technique était la solution miracle et je l'appliquai presque partout (comme un pattern). Or, pour l'exemple des performances citées, je me suis rendu compte que ce n'était pas forcément le cas. D'où ma question, est-ce un anti-pattern ?
    Vous avez je pense répondu de manière indirecte à cette question.
    Vous reconnaissez vous même qu'il a les vertus que je lui prêtait (pas visible par l’utilisateur, il est invariant, sans signification). Il n'est donc pas totalement à rejeter. Toutefois, il ne faut pas en abuser non plus et vous montrer pourquoi (l'histoire des accès disque et de certaines requêtes alors plus simple)


    Citation Envoyé par fsmrel Voir le message
    Question : pour cette même requête, combien de tables seront à joindre, à « trimballer » sans le mécanisme de l’identification relative ?
    Dans votre exemple, effectivement davantage. Comme quoi, ce qu'on "gagne" dans certaines requêtes, on le perd dans d'autre. Merci pour l'exemple.


    Citation Envoyé par fsmrel Voir le message
    J’ai noté que vous n’aimiez pas trop les clés multi-attributs, notamment parce que les jointures vous paraissaient bien lourdes ...
    Avec la clé primaire de la table Livraison, on n’en est pas loin... Mais un DBA se fera un plaisir de créer les vues occultant tout ça.
    Dans votre exemple, je serai curieux d'avoir un aperçu de ces vues ?
      1  0

  12. #12
    Expert éminent sénior
    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 812
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 812
    Points : 34 084
    Points
    34 084
    Billets dans le blog
    14
    Par défaut
    On sort un peu du sujet de cette discussion mais j'aimerais François, que vous expliquiez cette partie de votre dernier message :
    Je m’arrête un moment pour traiter de l’organisation de l’index « primaire » la table Client. Cet index a pour clé {ClientId}. Supposons que l’on soit dans le contexte d’une saisie de commandes intense. Si le prototype montre que plus les utilisateurs entrent en concurrence (disons aux heures de pointe, après la pause café-cigarette), plus les phénomènes de contention se manifestent, alors il faudra hacher ClientId plutôt que le valoriser séquentiellement. Par exemple, si pour le dix-millième client, la procédure de hachage fournit la valeur '5246901' pour la clé {ClientId} et la valeur '314' pour le client suivant, il est très probable que ces clients se retrouveront dans des pages (enregistrements) physiques éloignées (table space et index), d’où élimination des contentions. J’ai eu à traiter ce genre de problème pour un de mes clients, qui par la suite a commercialisé son application : très logiquement, plus la base de données de ses propres clients était réduite en volume et plus les problèmes de contention refaisaient surface...
    Cela signifie t-il que :
    - si des clients sont créés en série lors d'une période courte de temps,
    - et si les clients sont identifiés séquentiellement (identifiant auto-incrémenté),
    la construction de l'index fera que c'est potentiellement la même page d'index qui sera mise à jour et que cela va créer une sorte de bouchon comme aux péages des autoroutes du sud le premier week-end de juillet, chaque créateur de client devant "attendre" que cette page soit disponible pour que son client puisse être totalement enregistré par le SGBD ?

    Les arguments les plus convaincant dans votre message sont à mon avis ceux-ci :
    Cela dit, si vous voulez connaître les camions qui sont prévus pour les livraisons en attente concernant le client Tartempion, vous n’êtes pas obligé de faire mention dans la requête SQL de l’ensemble des tables que l’on vient de voir. Il suffira de procéder à la jointure des deux seules tables Client et Livraison (plus la table Camion).

    Question : pour cette même requête, combien de tables seront à joindre, à « trimballer » sans le mécanisme de l’identification relative ?
      0  0

  13. #13
    Rédacteur
    Avatar de benwit
    Profil pro
    dev
    Inscrit en
    Septembre 2004
    Messages
    1 676
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : dev

    Informations forums :
    Inscription : Septembre 2004
    Messages : 1 676
    Points : 4 265
    Points
    4 265
    Par défaut
    Citation Envoyé par CinePhil Voir le message
    Si je comprends bien, un client ne peut pas passer plus de 255 commandes ?
    Je ne sais pas comment sont stockées les données en base et c'est pour cela que je ne comprend pas la taquinerie.

    Mais ce qui différencie deux commandes d'un même client, c'est CommandId codé sur 2 octets ?
    2 octets = 2 exposant 16 = 65536 commandes différentes (ce qui lui en fait en gros 1 par jour pendant 180 ans), ça devrait suffire.
      0  0

  14. #14
    Membre averti
    Avatar de wafiwafi
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    500
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 500
    Points : 328
    Points
    328
    Par défaut Je ne vais être originale
    Je ne vais être originale, mais ce que je retiens de cette enrichissante discussion c'est qu'on ne peut pas réaliser un bon MCD sans que je ne récolte les conseils de l'implanteur (des choses qui risquent lui poser problème à priori).
    Il ne s'agit pas de se mêler de son travail ni de penser à des solutions d'implantation contrairement à ce qui a été dit. Mais je comprends la réaction.
    Personnellement, Je pense que Les choix de solutions doivent d'abord rendre compte du métier mais posent des fois des problèmes sur les performances de l'implémentation. Certes vous allez me dire que c'est le travail de l'implanteur. Je vous réponds que des fois, il est obligé de choisir la meilleure des mauvaises solutions si le MCD n'est pas revu. Si je peux éviter cela, personnellement, je n'hésite pas à consulter un dictionnaire (les miles conseils d'implanteur pour un concepteur) . En bref, je n'arrive pas à admettre l'idée de faire un meilleur MCD en m'interdisant des règles d'or un peu plus élargies et communes à tous les langages, pourquoi pas (j'idéalise!).
    Toujours pour moi, concevoir, certes ce n'est pas réaliser; mais des relations très subtiles existent malgré nous. Les déterminer sans mélanger les deux reste une autre paire de manche. C'est l'objet de ma question initiale!
    (c'est peu être un mirage, mais j'y crois)
    La question que je me pose: peut on éviter les problèmes d'implémentation sans faire de l'implémentation? (Tiens c'est un bon sujet de discussion!!)
    Cela reste un avis.
    Bien à vous
      0  0

  15. #15
    Expert éminent sénior
    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 812
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 812
    Points : 34 084
    Points
    34 084
    Billets dans le blog
    14
    Par défaut
    Citation Envoyé par wafiwafi Voir le message
    La question que je me pose: peut on éviter les problèmes d'implémentation sans faire de l'implémentation?
    Un cas que je vois souvent dans les schémas proposés dans ce forum ou ailleurs : la cardinalité minimale à 1 de chaque côté d'une association.
    Exemple :
    "Un service comprend au moins un employé et un employé est affecté à un seul service."

    De la règle de gestion ci-dessus, il découle le MCD :
    Employé -1,1----Affecter----1,n- Service

    Lorsque que je vais implanter ce schéma :
    - Si je crée un nouvel employé, je devrai renseigner à quel service il est affecté. Comme il a été embauché pour un nouveau service qui n'existe pas encore dans la BDD, je ne peux pas.

    Tant pis, je vais d'abord créer le service...
    - Si je crée un service, je devrai renseigner en même temps au moins une personne de ce service. Comme c'est un nouveau service et que son chef vient d'être embauché et n'est pas encore enregistré dans la table des employés, je ne peux pas.

    C'est le serpent qui se mord la queue à force de se demander qui de l'oeuf ou de la poule a commencé !

    Bien sûr, un trigger pourra gérer ce cas mais il est plus simple de transformer la règle de gestion trop stricte en :
    "Un service peut comprendre plusieurs employés et un employé est affecté à un seul service."

    Ce qui donne le MCD :
    Employé -1,1----Affecter----0,n- Service

    Et là je peux créer tous les services de l'entreprise avant de leur affecter les employés, ce qui est quand même plus pratique et correspond davantage à la réalité d'un nouveau système d'information.


    Donc la réponse à la question est, au moins en partie, OUI.
    Il suffit de prendre un peu de recul lors de la conception pour se demander si certaines règles de gestion, éléments du cahier des charges ou autres ne peuvent pas être assouplis pour garder une conception adéquate qui évitera des problèmes d'implémentation plus tard. Avec l'expérience, on y arrive facilement.
      0  0

  16. #16
    Membre averti
    Avatar de wafiwafi
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    500
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 500
    Points : 328
    Points
    328
    Par défaut Je suis très content de lire cela
    Bonjour,
    Je suis très content de lire cela; c'est un exemple plus que convaincant.
    C'est vrai que personnellement j'essaye d'éviter les 0 dans les relations pour les présences de la valeur NULL, mais là ça vaut le coup.
    De ce pas, j'entame la rédaction d'un petit dictionnaire et cette règle banale mais tellement importante le débutera.
    J'espère que d'autres règles viennent progressivement suite aux expériences des uns et des autres pour aider au maximum les modélisateurs et notamment les débutants.
    En tout cas merci à toi
      0  0

  17. #17
    Expert éminent sénior
    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 812
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 812
    Points : 34 084
    Points
    34 084
    Billets dans le blog
    14
    Par défaut
    Citation Envoyé par wafiwafi Voir le message
    C'est vrai que personnellement j'essaye d'éviter les 0 dans les relations pour les présences de la valeur NULL, mais là ça vaut le coup.
    Dans l'exemple que j'ai donné, le passage à la cardinalité 0 ne va donner de NULL dans les tables puisque la clé étrangère est dans la table des employés et non pas dans la table des services. Et du côté de l'employé, il y a toujours une cardinalité 1,1.

    Et si on a un couple de cardinalités (0,1 - 0,n), alors il vaut mieux créer une table associative, comme dans le cas du couple (0,n - 0,n). Et là il n'y a plus de NULL non plus.
      1  0

  18. #18
    Membre averti
    Avatar de wafiwafi
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    500
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 500
    Points : 328
    Points
    328
    Par défaut C'est exact
    Exact.
    Merci
      0  0

  19. #19
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 128
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 128
    Points : 31 677
    Points
    31 677
    Billets dans le blog
    16
    Par défaut
    Citation Envoyé par CinePhil Voir le message
    Un cas que je vois souvent dans les schémas proposés dans ce forum ou ailleurs : la cardinalité minimale à 1 de chaque côté d'une association.
    Exemple :
    "Un service comprend au moins un employé et un employé est affecté à un seul service."

    De la règle de gestion ci-dessus, il découle le MCD :
    Employé -1,1----Affecter----1,n- Service

    Lorsque que je vais implanter ce schéma :
    - Si je crée un nouvel employé, je devrai renseigner à quel service il est affecté. Comme il a été embauché pour un nouveau service qui n'existe pas encore dans la BDD, je ne peux pas.

    Tant pis, je vais d'abord créer le service...
    - Si je crée un service, je devrai renseigner en même temps au moins une personne de ce service. Comme c'est un nouveau service et que son chef vient d'être embauché et n'est pas encore enregistré dans la table des employés, je ne peux pas.

    C'est le serpent qui se mord la queue à force de se demander qui de l'oeuf ou de la poule a commencé !

    Bien sûr, un trigger pourra gérer ce cas mais il est plus simple de transformer la règle de gestion trop stricte en :
    "Un service peut comprendre plusieurs employés et un employé est affecté à un seul service."

    Ce qui donne le MCD :
    Employé -1,1----Affecter----0,n- Service

    Et là je peux créer tous les services de l'entreprise avant de leur affecter les employés, ce qui est quand même plus pratique et correspond davantage à la réalité d'un nouveau système d'information.
    Hum... Avec un MCD, on modélise la finalité, donc si selon le cahier des charges un service doit comporter au moins un employé, le schéma
    Employé -1,1----Affecter----0,n- Service
    dénature la représentation de l’information.

    Ça n’est qu’au niveau logique que vous déciderez d’affaiblir les règles de gestion des données, selon que vous déciderez de mettre en œuvre la contrainte ou pas, en accord évidemment avec qui de droit.

    A titre d'information, vous utiliseriez un SGBD conforme au Modèle Relationnel de Données, vous n’auriez pas d’état d’âme, la cardinalité 1,N n’aurait pas à être altérée pour des motifs de commodité d’utilisation de triggers et considérations du même tabac. En effet, le problème de l’oeuf et de la poule se résout très simplement : les INSERT qui servent à créer un service et son premier employé, sont empaquetés dans une liste (dont la fin est marquée par un point-virgule, tandis que les INSERT sont eux-mêmes séparés des virgules au sein de la liste. Le SGBD n’effectue ses contrôles qu’après détection du point-virgule, marqueur de fin de liste.

    (N.B. Ci-dessous, « EmployeId EmployeId 3 » se lit ainsi : l’attribut EmployeId est du type EmployeId et sa valeur est 3).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    INSERT Employe RELATION {TUPLE {
                                    EmployeId  EmployeId  3,
                                    ServiceId  ServiceId  1,
                                    Matricule  Matricule  103',
                                   }} ,  /* virgule : séparateur d’opérations */
    INSERT Service RELATION {TUPLE {
                                    ServiceId  ServiceId  1,
                                    EmployeId  EmployeId  3,
                                    ...        ...        ...
                                   }} ;  /* point-virgule : fin de liste */
    Tout cela est sous le capot et n'affecte donc pas le MCD...
      2  0

  20. #20
    Rédacteur
    Avatar de benwit
    Profil pro
    dev
    Inscrit en
    Septembre 2004
    Messages
    1 676
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : dev

    Informations forums :
    Inscription : Septembre 2004
    Messages : 1 676
    Points : 4 265
    Points
    4 265
    Par défaut
    Heureusement que des implémentations de SGBD ont prévu le cas !

    Si on peut parfois altérer le MCD (les fonctionnels ne sont pas sûr de leur régles), je serai assez d'accord avec fsmrel.

    Modifier la cardinalité 1-N en 0-N modifie la sémantique.
    On passe de "Le service n'a pas lieu d'exister s'il ne comporte pas d'employé" à 'Il peut exister des services fantômes" (A méditer)
      1  0

Discussion fermée
Cette discussion est résolue.
Page 1 sur 4 1234 DernièreDernière

Discussions similaires

  1. Quelle sont les base de DEV pour RPG en C# et XNA
    Par jumperx dans le forum XNA/Monogame
    Réponses: 3
    Dernier message: 11/02/2010, 17h24
  2. Réponses: 1
    Dernier message: 07/12/2007, 16h28
  3. [CSS] [FAQ] Quelles sont les règles de priorités entre CSS?
    Par BnA dans le forum Contribuez
    Réponses: 0
    Dernier message: 05/12/2007, 10h59
  4. Réponses: 6
    Dernier message: 03/07/2007, 11h34
  5. Réponses: 9
    Dernier message: 20/03/2007, 10h25

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo