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

Décisions SGBD Discussion :

Architecture table Client et Option


Sujet :

Décisions SGBD

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    511
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 511
    Points : 514
    Points
    514
    Par défaut Architecture table Client et Option
    Bonjour,

    Petite question sur l'architecture d'une BDD.
    J'ai une table client. Chaque client à des options (disons 5). Faut t-il mieux - 1) rajouter 5 colonnes boolean option1, option2; etc..
    2) Créer une table option avec référence vers le client.

    si je suis strictement les 3 premières FN, je me dis qu'option est lié uniquement à la clé primaire de client donc je dois les mettre dans client. Ai je raison ?

    Merci pour votre aide

  2. #2
    Membre averti Avatar de _Xavier_
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2009
    Messages : 311
    Points : 390
    Points
    390
    Par défaut
    Mieux vaut créer une table option à part. Apparemment tes options sont prédefinies ?

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    511
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 511
    Points : 514
    Points
    514
    Par défaut
    oui mes options sont prédéfinies. Au niveau perf il me semble mieux de faire une colonne option par options dans ma table client (pas de jointure).

    Au niveau modularité il me semble plus intéressant de faire une table options

  4. #4
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 113
    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 113
    Points : 31 590
    Points
    31 590
    Billets dans le blog
    16
    Par défaut
    Bonsoir,


    Citation Envoyé par Shivan Voir le message
    Au niveau perf il me semble mieux de faire une colonne option par options dans ma table client (pas de jointure).
    Si vous définissez une table ayant la structure suivante (clé primaire soulignée) :
    Client (ClientId, Option1, Option2, Option3, Option4, Option5, ClientNom, ClientAdresse, ...)
    La normalisation est respectée (au moins les 1NF, 2NF, 3NF, BCNF).

    Il est vrai par ailleurs que vous n’avez pas de jointure à faire pour tout savoir sur un client.

    Mais il ya quelques inconvénients :

    Dans le cas général, les fonctions d’agrégation SUM, AVG, COUNT, etc. ne peuvent pas être utilisées. Mais dans votre cas particulier, peut-être n’en avez-vous pas besoin.

    Un inconvénient majeur : la structure de la table est figée. Si un jour on ajoute des options, il faudra redéfinir cette structure et vraisemblablement reprendre toutes les requêtes qui font nommément référence aux attributs Option1, Option2, etc.

    Si vous définissez deux tables :
    Client (ClientId, ClientNom,.ClientAdresse, ...)

    Option (ClientId, OptionId, OptionValeur)

    La normalisation est respectée (OptionId est un numéro relatif, prenant des valeurs comprises entre 1 et 5, selon le nombre d’options retenues par le client).

    Si vous ne voulez pas passer votre temps à faire des jointures, vous pouvez créer une vue du genre :
    ClientOption (ClientId, ClientNom,.ClientAdresse, Option, ...)
    As

    SELECT x.ClientId, x.ClientNom,.x.ClientAdresse, y.OptionValeur...

    FROM Client x INNER JOIN Option y ON x.ClientId = y.ClientId
    Par ailleurs, le surcoût dû à la jointure est de l’ordre de la dizaine de millisecondes s’il y a accès au disque (par exemple dans le cas de DB2, un accès à l’index de service dont la clé contient les trois attributs ClientId, OptionId, OptionValeur).

    Les fonctions d’agrégation redeviennent opérationnelles.

    Avantage décisif (au moins dans le cas général) : si le nombre d’options passe de 5 à 6 ou plus, la structure des tables est inchangée, les requêtes ne sont pas à reprendre (sauf celles qui mentionnent explicitement le nombre 5).

    Maintenant, c'est vous qui voyez...

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    511
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 511
    Points : 514
    Points
    514
    Par défaut
    Merci pour cette excellente réponse, clair, complète avec les différentes avantages des solutions mises en évidence. C'est vraiment un plaisir de lire des réponses comme celle ci.

    Petite question subsidiaire :

    Si dans la table option Option (ClientId, OptionId, OptionValeur), je rajoute une colonne id qui serait un id auto incrémenté et deviendrait la clé primaire... je mérite combien de coup de fouet ?

    Merci

  6. #6
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 113
    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 113
    Points : 31 590
    Points
    31 590
    Billets dans le blog
    16
    Par défaut
    Bonsoir,

    Citation Envoyé par Shivan Voir le message
    Si dans la table option Option (ClientId, OptionId, OptionValeur), je rajoute une colonne id qui serait un id auto incrémenté et deviendrait la clé primaire... je mérite combien de coup de fouet ?
    Tarif syndical : vous mériterez 5 coups de fouet. La clé primaire étant déjà définie par le couple {ClientId, OptionId}, à quoi servirait d’en changer ?

    Le contenu de la table option devrait ressembler à ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    ClientId    OptionId   OptionValeur
       1           1           v11
       1           2           v12
       1           3           v13
      ...         ...          ...
       2           1           v21
       2           2           v22
       2           3           v23
      ...         ...          ...
    C'est-à-dire que l’incrémentation de OptionId commence à 1 pour chaque client. Mais si vous « oubliez » de repartir à 1, ne le dites à personne...

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 262
    Points : 12 936
    Points
    12 936
    Par défaut
    Bonjour,
    Je vois un autre avantage à utiliser une table d'option: en ajoutant un index sur optionId et optionValeur, la recherche de clients qui ont telle ou telle option est optimisée, alors qu'indexer les options directement dans la table client requière autant d'index que d'options, ce qui n'est vraiment pas "tiptop".

    Tatayo.

  8. #8
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 113
    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 113
    Points : 31 590
    Points
    31 590
    Billets dans le blog
    16
    Par défaut
    Bonjour,


    +1 pour tatayo.

    Juste une petite remarque. Pour optimiser la recherche des clients en fonction d’une option donnée, la clé de l’index ad-hoc serait plutôt la suivante :
    {OptionValeur, ClientId}
    En effet, en faisant figurer l’attribut ClientId à la suite de l'attribut OptionValeur, le système n’a plus besoin d’accéder à la table Option pour connaître les identifiants des clients. Quant à l’attribut, OptionId, sa présence n’apporte rien pour cette recherche.

    On pourrait quand même faire participer OptionId à la clé de cet index, en se disant qu’après tout ça ne coûte pas cher et ça permettrait d’optimiser par avance de futures requêtes auxquelles on n’a pas encore pensé :
    {OptionValeur, ClientId, OptionId}

  9. #9
    Membre éprouvé Avatar de Jester
    Inscrit en
    Septembre 2003
    Messages
    813
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 813
    Points : 1 057
    Points
    1 057
    Par défaut
    Citation Envoyé par fsmrel Voir le message
    Par ailleurs, le surcoût dû à la jointure est de l’ordre de la dizaine de millisecondes s’il y a accès au disque (par exemple dans le cas de DB2, un accès à l’index de service dont la clé contient les trois attributs ClientId, OptionId, OptionValeur).
    Hum, sauf que là il y a 5 jointure à faire. Soit 5 accès (ok ce n'est rien), sauf que ça quintuple le coût (vu que dans l'autre méthode il n'y a qu'un ou deux accès.

    Quand on fait des requêtes d'agrégats, ça commence à coûter cher la flexibilité. J'ai un début de problème à ce niveau sur un problème similaire.

  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 113
    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 113
    Points : 31 590
    Points
    31 590
    Billets dans le blog
    16
    Par défaut 5 X 1 = 1
    Bonsoir,


    Citation Envoyé par Jester Voir le message
    Hum, sauf que là il y a 5 jointure à faire. Soit 5 accès (ok ce n'est rien), sauf que ça quintuple le coût (vu que dans l'autre méthode il n'y a qu'un ou deux accès.
    -5 pour Jester.

    Vous sous-entendez que, si l’accès à une option d’un client coûte 10 millisecondes, le coût de l’accès aux 5 options de ce client sera de 50 millisecondes.

    Je ne l'entends pas ainsi. Tout d’abord, il n’y a pas 5 jointures, mais une seule jointure pour rapatrier toutes les options d’un client (il y en aurait 500, ça serait pareil), et le SELECT utilisé pour la vue que j’ai mentionnée dans mon premier message illustre cela :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    SELECT x.ClientId, x.ClientNom,.x.ClientAdresse, y.OptionValeur...
    FROM   Client x INNER JOIN Option y ON x.ClientId = y.ClientId
    Consciencieusement, DB2 (le SGBD que j’ai mentionné) accèdera au disque pour récupérer le tout (si ça n'est pas déjà en mémoire) : disons 20 millisecondes pour les données du client (index + table Client), plus 10 millisecondes (le surcoût dont je parlais dans mon message) pour récupérer les 5 options. Pourquoi seulement 10 millisecondes et pas 50 ? Tout simplement parce que les 5 options sont dans la même page (enregistrement physique) de l’index, donc un seul accès au disque suffit (avec DB2, la racine compte pour du beurre). En ma qualité de DBA, je vous invite à consulter la documentation de DB2 for z/OS pour approfondir le comportement de ce SGBD et bien comprendre comment les choses se passent sous le capot.

    Pour l'anecdote, quand je fais mes courses, je ramène en une seule fois le pack de bière, le pack d'eau minérale, le pack de whisky, le pack de calva et ma tablette de chocolat : je ne fais pas 5 voyages à 10 minutes chacun (le coffre de ma voiture est assez grand pour loger tout ça).

  11. #11
    Membre averti Avatar de _Xavier_
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2009
    Messages : 311
    Points : 390
    Points
    390
    Par défaut
    Merci fsmrel belle démonstration. Mais je ne partage pas tout.
    Pour faire la jointure le SGBD fait d'abord le build de la plus petite table, Option, qui tien sur une seule page. Ensuite pour faire le prob on charge la table Client, dont le nombre de tuples reste inconnu. Si cette table tient sur plus d'une page on aura plus d'une jointure normalement. Je me trompe ?

  12. #12
    Membre éprouvé Avatar de Jester
    Inscrit en
    Septembre 2003
    Messages
    813
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 813
    Points : 1 057
    Points
    1 057
    Par défaut
    En effet, sauf si vous voulez avoir toutes les options sur la même lignes, sinon ce n'est pas une solution identique à la solution 1 dont Shivan parlait.

    Ce n'est pas 5 accès lectures certes, mais ce n'est pas un accès lecture pour chaque nuplet de la table Client si l'on fait des aggrégats impliquant une lecture séquentielle de la table. On enlève de plus toutes les indirections des index.

  13. #13
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 113
    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 113
    Points : 31 590
    Points
    31 590
    Billets dans le blog
    16
    Par défaut Cinq colonnes à la une
    Bonsoir,


    Citation Envoyé par _Xavier_ Voir le message
    je ne partage pas tout. Pour faire la jointure le SGBD fait d'abord le build de la plus petite table, Option, qui tien sur une seule page. Ensuite pour faire le prob on charge la table Client, dont le nombre de tuples reste inconnu. Si cette table tient sur plus d'une page on aura plus d'une jointure normalement. Je me trompe ?
    A mon tour, je ne suis pas d’accord avec ce que vous écrivez. Je passerai rapidement sur la jointure, puis, parce que vous traitez du comment plutôt que du quoi, je vous montrerai comment les choses peuvent se passer sous le capot (sans toutefois approfondir). Sauf mention explicite, le SGBD dont je me sers est DB2 (plus précisément DB2 for z/OS), auquel j’ai déjà fait allusion dans mes précédents messages.

    De la jointure

    Vous utilisez le terme « jointure » pour ce qui manifestement est autre chose que l’opérateur défini dans le cadre du Modèle Relationnel de Données, opérateur fait pour manipuler des ensembles (comme tous les opérateurs relationnels du reste). Que le résultat d’une jointure comporte une ligne ou un million de lignes, il y a exactement une jointure qui est effectuée, ni moins, ni plus. En revanche, au niveau physique cette fois-ci, le nombre d’accès au disque ne sera évidemment pas le même pour récupérer deux cents lignes ou un million de lignes, comme on le verra. Mais, s’il vous plaît, ne mélanger pas la partie relationnelle (le quoi) et la partie algorithmique (le comment).

    Accès unitaires

    Avant de descendre dans la soute, je rappelle que dans mes précédents messages, j’ai traité de l’accès à un client en particulier, ainsi qu’à ses options. Pour accéder aux données d’un client (voire à l’ensemble des clients, du reste), on peut utiliser la vue ClientV dont je rappelle la structure (je renomme la table Option et l’appelle Element, car « Option » est un mot réservé en SQL) :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    CREATE VIEW ClientV (ClientId, ClientNom, OptionId, OptionValeur)
    AS  SELECT  x.ClientId, x.ClientNom, y.OptionId, y.OptionValeur
        FROM    Client x INNER JOIN Element y
                 ON  x.ClientId = y.ClientId ;

    Je rappelle aussi que la création de cette vue n’entraîne aucun accès aux données (pas plus qu’un CREATE TABLE), mais simplement son inscription au catalogue du SGBD (cf. par exemple les tables définies par la norme SQL, INFORMATION_SCHEMA.TABLES et INFORMATION_SCHEMA.VIEWS).

    Maintenant, pour accéder aux données du client ayant pour nom "Bernard", on exécute l’instruction suivante (les résultats seront présentés verticalement et non pas horizontalement, mais je crois avoir compris que là n’est pas le problème de Shivan) :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SELECT ClientId, ClientNom, OptionId, OptionValeur
    FROM   ClientV
    WHERE  ClientNom = 'Bernard' ;

    On peut quitter le Quoi et descendre dans la soute.

    On exécute cette requête après avoir bien sûr créé un index de clé {ClientNom, ClientId} pour accéder directement à la bonne page dans le table space hébergeant la table Client (voire se limiter à l’accès à l’index si l’on se contente de récupérer la valeur de ClientId) :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    CREATE INDEX ClientNomX2 ON Client (ClientNom, ClientId) ;

    Et après avoir aussi défini un index de clé {ClientId, OptionId, OptionValeur} pour accéder directement à la bonne page dans le table space hébergeant la table Element :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    CREATE INDEX ElementX2 ON Element (ClientId, OptionId, OptionValeur) ;

    Ces index ne se substituent pas aux index primaires (et clusters) des tables Client et Element, de clés respectives {ClientId} et {ClientId, OptionId}, bien qu’on puisse ruser, mais bon.

    Il évident que le SGBD a le souci de la parcimonie et ne chargera aucune table lors des SELECT, mais, dans un 1er temps, seulement une page (enregistrement physique) de l’index ClientNomX2, soit un coût d’accès de l’ordre de la dizaine de millisecondes. Dans un 2e temps, le SGBD récupérera les options de Bernard, contenues dans la page cible de l’index ElementX2, soit un coût supplémentaire de l’ordre lui aussi de la dizaine de millisecondes.

    A titre indicatif, voici ce qu’annonce SQL Server, qui adopte le même comportement que DB2 (exploitation des bons index, « y a pas de secret » comme disait l’autre) :



    Évidemment, le SGBD changera de stratégie s’il doit récupérer dans la table Client des données non indexées. En l'occurrence, le surcoût sera au tarif habituel, de l'ordre une dizaine de millisecondes.

    N.B. Je ne suis pas un fin spécialiste de SQL Server, mais quand j’aperçois l’expression « Index Seek », j’ai plutôt confiance.

    Vous évoquez l’encombrement de la table Option (que j’ai renommée Element comme je l'ai dit plus haut) :

    Pour en revenir à DB2 for z/OS : la table ne tiendra dans une seule page que si le nombre total d’options, tous clients confondus, est très faible. Pour fixer les idées, supposons qu’une page mesure 4 kilo-octets et qu’indépendamment des données propres au système, chaque ligne de la table occupe 14 octets, soit 4 octets pour la colonne ClientId + 2 octets pour la colonne OptionId + 8 octets pour la colonne OptionValeur (que l’on supposera compressée). Une page contiendra environ 200 lignes et il est clair que si la table en contient davantage, elle nécessitera plusieurs pages. Par exemple, pour une table de 30000 lignes, il faudra environ 150 pages.

    Pour sa part, l’index ElementX2 construit sur les 3 colonnes ClientId, OptionId et OptionValeur, consommera 1 page pour le niveau racine et 160 pages pour le niveau feuilles, chaque feuille contenant un peu plus de 190 clés de 14 octets chacune (au-delà de 30000 lignes, disons à concurrence de cinq millions de lignes, il faudra compter un niveau d’index en plus, ce qui induira un coût supplémentaire de 10 millisecondes pour récupérer les options d’’un client si elles ne sont pas déjà présentes en mémoire et encore 10 millisecondes au-delà, à concurrence de dizaines de milliards de lignes).

    Calons-nous sur une table de l’ordre de 30000 options. Pour retrouver celles d’un client en particulier, il faut donc compter une dizaine de millisecondes.

    Accès en masse

    Maintenant, si l’on veut retrouver toutes les options de tous les clients, on utilisera la requête suivante :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    SELECT ClientId, ClientNom, OptionId, OptionValeur
    FROM   ClientV ;

    Le SGBD changera de stratégie. Concernant la table Element, il procèdera à un balayage, soit des feuilles de l’index, soit du table space hébergeant la table. Mais cette-fois-ci, puisque la stratégie est séquentielle, DB2 lira les pages par rafales, et le coût unitaire passe à quelque chose comme 4 millisecondes, d’où un coût d’environ 150 * 4 = 600 millisecondes pour 30000 options. Pour un millions d’options : environ 5000 * 4 millisecondes, soit une vingtaine de secondes. Même principe pour la table Client. Pour savoir quelle stratégie DB2 utilisera (nested loop, merge scan, hybrid join, etc.), on exécutera une instruction EXPLAIN PLAN. Pour obtenir une performance optimale (coût total = somme des coûts Client et Element), on aura pris soin de définir les index « cluster » dont la clé a même 1er attribut, à savoir ClientId. Ce serait une erreur de procéder comme avait l’intention de le faire Shivan, je cite :

    Citation Envoyé par Shivan Voir le message
    Si dans la table Option (ClientId, OptionId, OptionValeur), je rajoute une colonne id qui serait un id auto incrémenté et deviendrait la clé primaire... je mérite combien de coup de fouet ?
    En effet, DB2 passerait son temps à recharger des pages qu’il a utilisées et qui auront pu disparaître de la mémoire (disons dans un contexte d’exploitation chargé, avec des tables comportant des dizaines de millions de lignes). Il y aurait inflation bien inutile des accès à 10 millisecondes chacun.

    A titre indicatif, voici la stratégie retenue par SQL Server :



    Quand j’aperçois l’expression « Clustered Index Scan », quelque part je suis rassuré...


    Je propose que nous sortions de la soute.

  14. #14
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 113
    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 113
    Points : 31 590
    Points
    31 590
    Billets dans le blog
    16
    Par défaut Indépendance physique et logique des donnée
    Bonsoir à nouveau,


    Citation Envoyé par Jester Voir le message
    En effet, sauf si vous voulez avoir toutes les options sur la même lignes, sinon ce n'est pas une solution identique à la solution 1 dont Shivan parlait.

    Ce n'est pas 5 accès lectures certes, mais ce n'est pas un accès lecture pour chaque nuplet de la table Client si l'on fait des aggrégats impliquant une lecture séquentielle de la table. On enlève de plus toutes les indirections des index.
    1) Nuance. Shivan n’a pas précisé s’il s’intéressait à l’aspect cosmétique des choses (voyez à ce sujet les rappels d'al1_24), à savoir s’il fallait présenter les options du client verticalement ou horizontalement. Il parlait de la façon de structurer les tables d’une base de données relationnelle, ce qui est quand même différent.

    2) Shivan n’a pas parlé d’agrégats. En plus vous tricotez les niveaux : le niveau logique (production d’agrégats) et le niveau physique (lecture séquentielle, « indirection » des index). Il est préférable de parler de ces choses chacune à leur tour, sinon on n'en sort pas, on en arrive à des sophismes tels que celui qui a pour objet la fabrication d’« agrégats impliquant une lecture séquentielle ».

    Cela dit, revenons sur l’aspect parcimonie des accès au disque que je viens d’évoquer dans mon précédent message, à l’attention de _Xavier_, et redescendons dans la soute.

    Je répète donc que pour obtenir toutes les données d’un client, y compris ses options, il faut tabler sur une vingtaine ou une trentaine de millisecondes (sachant que si l’on accède à 30 clients logés dans une même page, même chose pour leurs options, le surcoût en accès disque est nul, voyez mon précédent message à ce sujet).

    Voyons voir. Je rappelle d’abord la définition de la vue ClientV :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    CREATE VIEW ClientV (ClientId, ClientNom, OptionId, OptionValeur)
    AS  SELECT  x.ClientId, x.ClientNom, y.OptionId, y.OptionValeur
        FROM    Client x INNER JOIN Element y
                 ON  x.ClientId = y.ClientId ;

    Pour afficher les options du client "Bernard" (présentation verticale) en admettant que deux clients n’ont pas même nom (sinon on utilisera plutôt une donnée du genre numéro de Siret) :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT ClientNom, OptionValeur
    FROM   ClientV
    Where  CLientNom = 'Bernard' ;

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    ClientNom  OptionValeur
    ---------  ------------
     Bernard    Option B1
     Bernard    Option B2
     Bernard    Option B3
     Bernard    Option B4
     Bernard    Option B5
    Maintenant, si l’on veut à tout prix afficher les options sur la même ligne (lubie du chef par exemple, en espérant que ça tiendra dans la largeur d’une ligne d’une page A4), ça n’est quand même pas bien compliqué et le surcoût en accès disque reste nul. Par exemple, avec Transact-SQL (SQL Server), pour afficher les options du client "Bernard", on en passe par un curseur appliqué à la vue et le tour est joué.

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    DECLARE @TheClient VARCHAR(32), @TheVariableCLient VARCHAR(32)
    DECLARE @TheListe  VARCHAR(200)
    DECLARE @ClientNom Varchar(32), @OptionValeur varchar(32)
     
    SET @TheVariableCLient = 'Bernard'
    SET @TheListe = ''
     
    DECLARE TheCurseur CURSOR  FOR 
            SELECT ClientNom, OptionValeur
            FROM   ClientV
            WHERE  ClientNom = @TheVariableCLient 
     
    OPEN TheCurseur
    FETCH From TheCurseur INTO @ClientNom, @OptionValeur
     
    WHILE @@FETCH_STATUS = 0
          BEGIN
           SET @TheClient = @ClientNom 
           SET @TheListe = @TheListe + @OptionValeur + ', ' 
             FETCH From TheCurseur INTO @ClientNom, @OptionValeur
          END
    CLOSE TheCurseur
     
    SELECT  @TheClient AS Client, @TheListe As ListeOptions
     
    DEALLOCATE  TheCurseur

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Client    ListeOptions
    ------    ------------
    Bernard   Option B1, Option B2, Option B3, Option B4, Option B5,
    Ça n’est pas la mer à boire, et le coût en accès est toujours de l’ordre de la vingtaine de millisecondes (synchronisation des index clusters oblige). Maintenant, si le chef veut voir les options de tous les clients, on raffine le code et le plus simple est d’utiliser un sac pour stocker le résultat (un sac c’est comme une table, mais ça n’est pas un ensemble). Il en résulte un léger surcoût, mais ça ne va pas bien loin.

    Présentation verticale :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT ClientNom, OptionValeur
    FROM   ClientV ;

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ClientNom  OptionValeur
    ---------  ------------
     Alain      Option A1
     Alain      Option A2
     Alain      Option A3
     Bernard    Option B1
     Bernard    Option B2
     Bernard    Option B3
     Bernard    Option B4
     Bernard    Option B5
     Charles    Option C1
    Création du sac :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    CREATE TABLE PresentationHorizontale
    (
          ClientNom             VARCHAR(32)     NOT NULL
        , ClientListeOptions    VARCHAR(200)    NOT NULL
    ) ;

    Nouvelle version du curseur (on admet une fois de plus que deux clients n’ont pas le même nom) :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    DECLARE @TheClient varchar(32), @TheClientOld varchar(32)
    DECLARE @TheListe varchar(200), @TheListeOld  varchar(200)
    DECLARE @ClientNom Varchar(32), @OptionValeur varchar(32)
     
    DECLARE TheCurseur CURSOR FOR 
            SELECT ClientNom, OptionValeur
            FROM   ClientV
     
    SET @TheListe = ''
    SET @TheClient = ''
     
    OPEN TheCurseur
    FETCH From TheCurseur INTO @ClientNom, @OptionValeur
     
    WHILE @@FETCH_STATUS = 0
          BEGIN
           SET @TheClientOld = @TheClient
           SET @TheListeOld = @TheListe
           IF  @TheClientOld <> @ClientNom AND @TheClientOld <> ''
               BEGIN
                   INSERT INTO PresentationHorizontale 
                         SELECT @TheClientOld AS Client, @TheListeOld As ListeOptions
                   SET @TheListe = ''
               END   
           SET @TheClient = @ClientNom 
           SET @TheListe = @TheListe + @OptionValeur + ', ' 
             FETCH From TheCurseur INTO  @ClientNom, @OptionValeur
          END
    CLOSE TheCurseur
     
    INSERT INTO PresentationHorizontale 
          SELECT @ClientNom AS Client, @TheListe As ListeOptions
     
    DEALLOCATE  TheCurseur ;

    Contenu du sac :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    ClientNom  ClientListeOptions
    ---------  ------------------
     Alain     Option A1, Option A2, Option A3, 
     Bernard   Option B1, Option B2, Option B3, Option B4, Option B5, 
     Charles   Option C1,
    Les pros de Transact-SQL amélioreraient le code, mais il s’agit déjà de montrer qu’à partir d’une structure saine des tables, cela ne coûte pas cher de procéder à leur jointure et en plus de présenter un résultat à à l’italienne...

    Maintenant, si l’on veut rechercher les clients qui utilisent telle ou telle option, je crois que la structure saine des tables conviendra parfaitement. Sinon, comme dit très justement tatayo, bonjour les index et la dégradation des performances lors des mises à jour :

    Citation Envoyé par tatayo Voir le message
    indexer les options directement dans la table client requière autant d'index que d'options, ce qui n'est vraiment pas "tiptop".

  15. #15
    Membre habitué Avatar de Mygush
    Inscrit en
    Juin 2007
    Messages
    226
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 226
    Points : 175
    Points
    175
    Par défaut
    Bonjour à tous,
    En cherchant une solution à mon problème, je tombe sur cette discussion qui m'a débloqué en partie...
    En revanche, en testant le code de fsmrel, cela fonctionne parfaitement dans le cas où j'ai un client en particulier mais dans le cas où deux clients n'ont pas le même nom, je n'ai pas réussi à obtenir le résultat annoncé pour le contenu du sac à savoir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ClientNom  ClientListeOptions
    ---------  ------------------
     Alain     Option A1, Option A2, Option A3, 
     Bernard   Option B1, Option B2, Option B3, Option B4, Option B5, 
     Charles   Option C1,
    En effet, le sac que j'obtiens ressemble à cela:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ClientNom  OptionValeur
    ---------  ------------
     Alain      Option A1
     Alain      Option A2
     Alain      Option A3
     Bernard    Option B1
     Bernard    Option B2
     Bernard    Option B3
     Bernard    Option B4
     Bernard    Option B5
     Charles    Option C1
    Est-ce une erreur de ma part où alors un problème dans le script ?
    Merci d'avance pour vos réponses !

  16. #16
    Membre averti Avatar de _Xavier_
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2009
    Messages : 311
    Points : 390
    Points
    390
    Par défaut
    La différence ne réside que dans l'affichage. Le premier tableau sert juste d'illustration mais quand tu fais une requête Select sur ta base les données apparaissent sous forme d'un schéma ligne/colonne. Au niveau applicatif tu peux les manipuler comme tu veux.

  17. #17
    Membre habitué Avatar de Mygush
    Inscrit en
    Juin 2007
    Messages
    226
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 226
    Points : 175
    Points
    175
    Par défaut
    Merci de ta réponse _Xavier_ mais en fait mon problème est le suivant :
    Je souhaite que ma requête retourne toutes les options d'un client sur une ligne.
    J'arrive à obtenir ce résultat avec un client en particulier mais lorsque je souhaite afficher tous les clients, ça ne fonctionne plus...
    Il y a certainement quelque chose que je ne fais pas comme il faut...

  18. #18
    Membre averti Avatar de _Xavier_
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2009
    Messages : 311
    Points : 390
    Points
    390
    Par défaut
    Citation Envoyé par Mygush Voir le message
    Merci de ta réponse _Xavier_ mais en fait mon problème est le suivant :
    Je souhaite que ma requête retourne toutes les options d'un client sur une ligne.
    Le problème comme je te l'ai dit n'a rien à voir avec le modèle. Ce que tu veux est plutôt lié au langage Sql. Pour y arriver tu auras autant de jointures que d'options, avec des cases à NULL pour les clients qui n'ont pas toutes les options. Tu auras aussi besoin d'une colonne NumOption pour les options de même numéro de choix .

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Select ClientNom  From Client AS Client LEFT OUTER JOIN 
    (Select NomOption From Option where NumOption = 1) AS Option1 LEFT OUTER JOIN 
    (Select NomOption From Option where NumOption = 2) AS Option2 LEFT OUTER JOIN 
    ...
    C'est pas très pratique, mieux vaut faire ce travail au niveau applicatif, Java, php, ...

Discussions similaires

  1. Conception de mon architecture Tables & Relations
    Par hurricane66 dans le forum Modélisation
    Réponses: 13
    Dernier message: 15/10/2007, 20h16
  2. Architecture réseau client/serveur
    Par youp_db dans le forum Algorithmes et structures de données
    Réponses: 1
    Dernier message: 28/08/2006, 19h13
  3. [EJB3] Architecture application client lourd
    Par kristof_machin dans le forum Java EE
    Réponses: 3
    Dernier message: 19/04/2006, 13h36
  4. [Architecture] communication client/serveur client/client
    Par daed dans le forum Général Java
    Réponses: 4
    Dernier message: 28/01/2006, 23h23
  5. [Architecture][Strategie] Client/Serveur Choix d'une architecture
    Par etiennegaloup dans le forum Général Java
    Réponses: 7
    Dernier message: 06/07/2005, 12h00

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