De l'utilité des relations (1,1) en BDD

muqaddarmuqaddar Administrateur
09:21 modifié dans Actualités #1
Salut à  tous !

Je suis en train de réaliser la modélisation d'un site.
Il y a une table qui contient plus de 70 champs, dont une vingtaine est obligatoire, les autres sont faculatifs.
Ils seront remplis dans des formulaires contenus dans des onglets.

Je n'ai jamais eu besoin de faire de relations (1,1) en BDD mais là  je me pose la question la question de l'utilité de faire plusieurs tables au lieu d'une seule.
- table principale : contient 20 champs
- et chaque autre table contient 10 champs regroupés par "type".

Donc la table principale a une clé secondaire vers les autres... relation 1,1...

Peut-on me confirmer qu'on peut en tirer un avantage en ressources lors des requêtes sql... quand on fait un "select *" par exemple, parce que cette solution complique les requêtes...
Sinon, quels autres avantages à  part la lisibilité ?

Réponses

  • AliGatorAliGator Membre, Modérateur
    09:21 modifié #2
    Hello,

    - Tu y gagnes en lisibilité de ta BDD

    - Tu y gagnes aussi en espace utilisé par ta base, puisqu'au lieu d'avoir 70*N champs stockés (N = nombre de fiches), tu as 20*N+50*M champs, avec M nombre de fiches pour lequelles tu utilises les champs supplémentaires. Comme M<<N tu commences vite à  y gagner : Par exemple sur 100 fiches avec seulement 4 ayant les champs facultatifs, ça fait 20*100+4*50 = 2200 champs au lieu de 7000... Alors surtout si les champs en question sont larges (BLOB, Texte, ...) ça vaut encore plus le coup.

    - Et tu peux à  la limite enfin optimiser tes requêtes : tu ne fais la requête de tes 50 champs optionnels QUE s'ils existent. SQL n'aura à  faire correspondre les champs  que pour 20 champs dans la plupart des cas, pas 70. En contrepartie tu fais 2 requêtes au lieu d'une pour tes fiches où tu as les champs optionnels, mais comme ça arrive bien moins souvent...

    Par contre évidemment évite d'utiliser un JOIN pour joindre tes 2 tables, car tu perdras alors la performance gagnée voire pire, vu que si tu fais un JOIN tu retrouves tes 70 champs quelle que soit la fiche demandée même si elle n'a pas de champ optionnels (ils seront alors à  NULL), donc comme maintenant, mais en plus il faut qu'il effectue la jointure !

    Tu peux alors faire tes 2 requêtes dans tous les cas, donc interroger les 2 tables à  tous les coups : s'il ne trouve pas la fiche dans la table de champs optionnels, ça va pas prendre bcp de temps, puisqu'il ne te retournera aucune fiche, rien à  faire correspondre... donc tu gagneras logiquement en temps ; et s'il trouve tu auras les infos comme attendu.
    Ou alors, tu peux à  la limite intégrer un COUNT(...) lors de ta première requête pour savoir au passage s'il y a une fiche dans la 2e table (et si c'est pas le cas te passer de faire la 2e requête), mais le COUNT qui va regarder dans la 2e table pour voir s'il y a une fiche de champs facultatifs à  interroger va sans doute faire perdre plus de temps que de faire la requête de toute façon (et voir au retour de la requête si tu récupères zéro ou une fiche)... donc autant faire les 2 requêtes dans tous les cas :)

    L'avantage de séparer c'est que s'il n'y a pas de fiche dans la table de champs optionnels, la 2e requête ne retournera aucune fiche, donc pas de temps de perdu à  matcher les 50 champs inutiles avec ton interface mysql (récupérer les valeurs dans la fiche, les faire correspondre avec ton objet en ObjC que tu utilises pour manipuler tes données dans ton code... etc)

    En tout cas tu ne peux pas spécialement y perdre, le seul inconvénient étant d'être du coup obligé de faire 2 requêtes au lieu d'une, mais à  mon avis ce que tu y gagnes à  côté, tant en temps dans les cas où y'a pas de champs optionnels qu'en lisibilité & co, les vaut largement ! ;)
  • muqaddarmuqaddar Administrateur
    09:21 modifié #3
    Merci AliGator de ton explication très claire ! :)

    Je m'oriente actuellement vers une table principale de 20 champs, et 5 tables supplémentaires de 10 champs (chaque table contient des données du même acabit).

    Si j'ai bien compris, ce sont mes tables supplémentaires qui doivent pointer vers ma table principale, et non l'inverse.
    Donc, table1, table2... ont des foreign keys vers maintable_id.

    Je ferai donc :
    select * FROM maintable
    select count(id) FROM table1 WHERE table1.maintable_id=maintable.id
    if (mysql_num_rows(requete_comptage) > 0)
      select * from FROM table1 WHERE table1.maintable_id=maintable.id

    Est-ce que c'est ça le principe ou je me foire ?
  • AliGatorAliGator Membre, Modérateur
    09:21 modifié #4
    A peu près, à  quelques erreurs SQL près.

    Là  pour ton code tu fais 3 requêtes SQL alors que tu pourrais te contenter de 2. En plus le test effectué sur la 2e est faux...

    1) Tu demandes les données principales. Ca on est d'accord.
    2) Mais ensuite tu t'embrouilles :
    • D'une part ta condition "table1.maintable_id = maintable.id" suppose que tu aies dans ta requête 2 références de tables, une "table1" et une "maintable", ce qui n'est pas le cas, puisque tu ne fais ton SELECT que sur table1.
    • En plus tu fais une requête qui, si la condition WHERE est vérifiée, te retournera une fiche avec un unique champ s'appellant "COUNT(id)" et contenant le nombre d'ID différents, donc il aura toujours la valeur 1 ; et si la condition WHERE n'est pas vérifiée, il ne retournera aucune fiche. Donc tu utilises COUNT et WHERE alors qu'un seul des 2 aurait suffi, et COUNT n'a pas trop d'intérêt alors ici vu que tu ne l'utilises pas. Tu demandes à  SQL de faire un calcul supplémentaire inutile !
    • Et enfin cette requête est nullement nécessaire : il suffit de faire un "SELECT * FROM table1 WHERE maintable_id = 5" qui, s'il ne trouve rien, ne renverra aucune fiche, donc tu sauras que tu n'as pas d'infos facultatives dans cette table pour la fiche que tu traite, et s'il trouve qqch il te donnera les infos que tu veux.


    Au final à  mon avis le plus efficace, comme expliqué dans mon premier post, c'est de faire les requêtes sur les tables d'infos facultatives dans tous les cas : s'il ne trouve pas de fiche portant l'ID que tu recherches, il ne te renverra aucune fiche, donc ce sera très rapide, sinon il t'en renverra une fiche unique avec les infos que tu veux. Tu fais donc d'une pierre 2 coups, tu sais à  la fois s'il y a des infos facultatives, et s'il y en a en plus tu les récupères, tout ça en une seule requete SQL.


    Ci dessous un bout de code PHP (jamais trop fait de SQL ailleurs qu'en PHP), je te laisse traduire le nom des méthodes et le code si tu utilises autre chose.
    $mainReq = mysql_query(&quot;SELECT * FROM maintable&quot;); // là  tu as toutes tes fiches principales.<br />// Ensuite, pour chacune des fiches, tu regardes s&#39;il y a des infos facultatives :<br />while( false !== ($mainRec = mysql_fetch_array($mainReq)) )<br />{<br />  $mainID = mainRec[&#39;id&#39;];<br />  $req1 = mysql_query(&quot;SELECT * FROM table1 WHERE maintable_id = {$mainID}&quot;);<br />  $info1 = mysql_fetch_array($req1); // on lit la première fiche retournée.<br />  // Comme ça renvoie false s&#39;il n&#39;y a plus de fiche (donc dans notre cas si on n&#39;avait aucune fiche résultat)<br />  // ben on peut facilement tout tester !<br /><br />  // dans $mainRec tu as les champs de ta table principale<br />  // $info1 vaut soit false (si pas d&#39;infos de la table1 pour la fiche principale courante<br />  // et contient les champs de la table1 sinon (correspondant à  la fiche courante)<br />} // fiche suivante
    


    Maintenant si tu découpes trop, faut aussi faire gaffe, car tu vas gagner un peu dans le sens où MySQL va avoir moins de champs à  faire correspondre, mais tu vas perdre un peu parce que tu seras obligé de faire plus de requêtes... Il faut trouver l'équilibre, ça dépend du pourcentage de fiches qui auront des infos facultatives...
  • muqaddarmuqaddar Administrateur
    09:21 modifié #5
    Merci d'avoit tout détaillé.
    J'avais compris le principe, mais mal écrit mes requêtes à  la va-vite.

    Je vais tâcher de ne pas trop découper... pour ne pas faire trop de requêtes.

    Bonne journée à  toi Ali.
  • HurricanHurrican Membre
    09:21 modifié #6
    Permet moi Ali, de ne pas te suivre entièrement, en tout cas sur le join.
    Si tu as créé des "foreign keys", le temps d'accès sera quasiment identique à  l'accès d'un seul enregistrement, soit bien inférieur à  deux accès. En contrepartie, bien sûr, c'est la mise à  jour qui est un peu plus lente, mais c'est négligeable, surtout quand on voit les avantages qu'on peut en tirer.
    De plus, avec un "Left Join", tu t'assures de ne pas récupérer plus de données que nécessaire. ;)
    Le remplissage par des Null, des zones sans correspondance est on ne peut plus rapide, en tout cas autrement plus rapide que la mise en place d'un 2ème pointeur SQL.
    J'utilise çà  sur des bases énormes, et avec le join je défile mes fichiers bien plus rapidement que sans ! Testé avec DB2/UDB, DB2, MySQL, PostGreSQL. ;)

  • schlumschlum Membre
    09:21 modifié #7
    Oui, les jointures sont très optimisées dans les bases de données !
    C'est mieux de faire un JOIN que de faire deux requêtes.
  • AliGatorAliGator Membre, Modérateur
    09:21 modifié #8
    dans 1172249054:

    Permet moi Ali, de ne pas te suivre entièrement, en tout cas sur le join.
    Si tu as créé des "foreign keys", le temps d'accès sera quasiment identique à  l'accès d'un seul enregistrement, soit bien inférieur à  deux accès. En contrepartie, bien sûr, c'est la mise à  jour qui est un peu plus lente, mais c'est négligeable, surtout quand on voit les avantages qu'on peut en tirer.
    Yep, je confirme, tout bien réfléchi, je ne suis pas d'accord avec Ali moi non plus* ;) les JOIN sont suffisament optimisés pour que ça ne pénalise pas finalement la requête...

    *mais ne lui répétez pas, ça risquerai de le vexer  :) ;D
Connectez-vous ou Inscrivez-vous pour répondre.