Structure BDD d'entreprises de matériaux de BTP

Le problème exposé dans ce sujet a été résolu.

Salut tout le monde,

Je souhaite recueillir votre avis sur ma structure de BDD. Avant de vous expliquer mon besoin, je vous précise qu’actuellement je souhaite travailler un peu cette structure de BDD et éventuellement la remplir, mais que je n’ai pas prévu pour l’instant de l’exploiter dans le sens d’en créer une interface. J’utilise pour l’instant LibreOffice Base et je verrai ce que je réussirai à faire un jour, ou je la donnerai à d’autres.

Certains m’ont déjà vu parler de ce que j’ai fait pendant mon stage dernier. J’ai travaillé sur les filières locales de matériaux durables pour le BTP, comprendre les matériaux biosourcés, recyclés, ou de réemploi. J’ai notamment consulté des listes et des listes d’entreprise pour en faire un tableau lui-même exploité dans un Power BI. L’idée global d’avoir un chantier dans telle région et de chercher dans la BDD quelles sont les entreprises qui peuvent proposer tels matériaux, de telles origines, pour tel usage.

Schéma actuel de la BDD
Schéma actuel de la BDD

Est-ce que vous pourriez me dire ce que vous en pensez ?

Quelques petites précisions :

  • Les liens sont bien affichés de 1 vers n, il faut éventuellement agrandir pour le voir.
  • Les clefs primaires sont indiquées avec un petit symbole de clef orange. De toute façon, chaque table a actuellement un champ Id qui sert de clef primaire.
  • Au niveau des types, j’ai des Int (pour les Id, notamment), de mémoire quelques SmallInt, beaucoup de texte (VARCHAR), rarement des textes sans casse (VARCHAR_IGNORECASE, ou un truc du genre), et un "mémo" (LONGVARCHAR je crois) pour la description d’entreprise.

Pour le fonctionnement, donc, il s’agit de remplir en priorité la table Entreprises. Pas mal de champs sont prévus, mais souvent optionnels. On m’a déjà conseillé de reporter les champs concernant l’adresse sociale vers la table Adresses, avec une relation depuis la table Entreprises via l’Id de l’adresse.

Il est prévu de pouvoir avoir plusieurs adresses. Certaines entreprises ont des bureaux dans plusieurs coins. Plus important, il y a souvent plusieurs sites de production. Le champ EstProduction permet de distinguer les sites de production des simples bureaux administratifs.

La table Produits est la table la plus importante. Chaque contient l’entreprise, un type de produit (pendant mon stage, on s’est en parti basé sur la base Inies pour les types, par exemple Isolation, ou Poteau), et une indication de si ce produit est disponible en biosourcé, recyclé ou réemploi. J’aurais pu faire plus simple, mais j’ai trouvé intéressant de dinstiguer selon chaque produit s’il est recyclé ou biosourcé, et non pas de manière globale pour tous les produits de l’entreprise.

Les types de produits sont une table qui va être globalement fixe. C’est en gros une liste, qui peut éventuellement être amenée à changer, mais en gros ça va être fixe. Elle permet à la table Produits de venir piocher dedans. Le champ Ordre est destiné à trier l’affichage des types pour la sélection. La table CatégoriesTypes sert à catégoriser les types de produits, pour faciliter l’insertion et la recherche dans les données.

Dans le même genre, j’ai un tableau Biosource qui sera également une liste de matériaux biosourcés (chanvre, lin, paille, bois, etc.). Il y a une table LienBiosource qui permet de faire le lien entre les entreprises et les matériaux biosourcés qu’elle utilise. On n’aura pas une précision par produit, mais je ne pense pas que ce soit nécessaire parce que sinon ça va être chiant à remplir pour un apport pas forcément intéressant.

Enfin, une table Contacts qui viendra contenir tous les contacts publics pour les entreprises. Lié à l’entreprise, puis des champs classiques, je ne pense pas qu’il y en ait besoin de plus.

Merci à tous !

+0 -0

Salut Phigger,

Tout d’abord, il faut bien distinguer la manière d’organiser la base de données avec la façon dont les données dans celle-ci seront présentées à l’utilisateur. Par exemple, l’attribut Ordre ne devrait pas figurer sur ce schéma, puisqu’il n’a de sens que lors de l’affichage des données.

Ensuite, tu dis que les entreprises ont plusieurs bureaux, certains administratifs, et d’autres de production. Cela me semble être une entité importante, que l’on pourrait représenter par une table Sites. On aurait donc une table Entreprises qui aurait 1 à n sites, et chaque site serait lié à 1 et 1 seule entreprise.

Ces sites auraient un attribut TypeSite qui serait une clé étrangère (une référence) vers une table TypesSite qui aurait pour attribut {Id, Type}. D’après ta description, je vois pour l’instant 2 types possibles : Production et Administration. Du coup, tu n’aurais plus d’attribut EstProduction dans la table Adresse, ce qui fait sens puisque cette propriété est attachée au site d’une entreprise et non à son adresse. Peut-être aussi un troisième type Siège ?

Pour la table Produits, l’idée d’une table TypesProduit me semble effectivement très pertinente. Je n’ai par contre pas compris l’utilité de la table CategoriesType. Pour ce qui est des attributs EstRecycle, EstReemploi et EstBiosource, est-ce qu’ils ne sont pas communs à tous les produits d’un même type produit ? Sinon, est-ce qu’un produit peut avoir plusieurs de ces attributs à vrai en même temps ? Par exemple, un produit peut-il être à la fois recyclé et biosourcé ?

En ce qui concerne la table LieuProduction, si tu suis ma proposition sur la table Sites, elle serait alors liée entre celle-ci et la table Produits. Il me semble inutile de lui donner un attribut Id, puisqu’elle contient déjà des identifiants qui sont propres à ta base de données. La clé de cette table serait donc composée des deux attributs IdProduit et IdAdresse. Idem pour la table LienBiosource.

Pour la table Biosource, il faudrait peut-être précisé que cela concerne seulement les matériaux, j’ai pensé au début une sorte de redite ou de duplication avec l’attribut EstBiosource de la table Produits.

Dans le schéma, la table Contacts est liée à la table Produits au lieu de la table Entreprises.

Enfin sur les attributs optionnels, il faut à mon avis leur porter une attention particulière. Il est toujours difficile de prévoir et gérer correctement les cas où l’attribut est nul. Une solution pourrait être, pour une entité, de créer une table séparée contenant une clé étrangère vers cette dernière et uniquement les attributs optionnels. Pour prendre un exemple hypothétique, supposons que les attributs Complement1 et Complement2 de la table Adresses soient optionnels. Plutôt que de le mettre directement dans celle-ci, on pourrait créer une table AdresseAvecComplements qui aurait pour attributs {IdAdresse, Complement1, Complement2} (voire même {IdAdresse, Complement, NumeroComplement}). Cela implique cependant une (légère) complexification du schéma de la table ainsi qu’une jointure supplémentaire, mais, à mon avis, cela capture mieux le fait que certaines adresses ont complément mais pas d’autres. Cela dit, l’exemple de l’adresse n’est peut-être pas le plus pertinent.

+0 -0

Salut, et merci pour le retour ! Avant de te répondre, je vais juste préciser que j’ai viré les champs d’adresse sociale, après discussion sur le discord et réflexion, c’est vraiment pas une info utile, et ça simplifie d’autant la table.

Je pense que je comprends ce que tu veux dire pour l’ordre. Dans ce cas, où est-ce que je suis censé configurer ça ? Mettons dans un site web qui afficherait ces données, il me faudrait en back-end un fichier de conf qui donnerait cet ordre ? Concernant les catégories, c’est lié, puisqu’en fait c’est le niveau du dessus du tri (une catégorie contient plusieurs types, par exemple la catégorie structure va contenir les poteaux, les poutres, les murs, les dalles, etc.)

Ce que tu appelles Sites est effectivement ce que moi j’ai nommé Adresses par habitude. Il y a effectivement plusieurs adresses par entreprise, et 1 adresse = 1 site, en gros. Du coup, je ne suis pas sûr de comprendre ce que tu proposes avec la table Sites ?

Dans ma table Produits, je ne compte pas référencer tous les produits de chaque entreprise (par exemple, je ne vais pas lister chaque isolant d’Isonat, mais juste qu’Isonat fait des isolants classiques, des isolants répartis, etc. sinon ce serait un boulot monstrueux qui ne sera jamais à jour. Or, une boîte qui fait plusieurs produits pour un même usage (par exemple, justement, plusieurs isolants), en propose parfois des versions biosourcés ou des versions recyclés. C’est pour éviter de faire plusieurs lignes dans la table, et je pensais également à la manière de saisir les données. Quand je rentre qu’Isonat fait des isolants, je veux directement pouvoir cocher s’ils en proposent des recyclés ou des biosourcés (vraiment avec des cases à cocher, ce qui colle plutôt aux booléens) et non pas rentrer une ligne pour ensuite rentrer la même ligne avec ce seul changement.

Pour la table LieuProduction, je note ce que tu dis, mais je ne sais pas comment ça marche. Tu prendrais comme clef primaire une chaîne de caractères en concaténant les deux autres clefs ? Est-ce que c’est grave d’avoir un Id même s’il n’est pas vraiment exploité ?

Je note que mon explication pour Biosource n’était pas claire, mais normalement à l’usage il devrait y avoir des formulaires qui rendraient ça plus simple.

Pour la table Contacts, c’est juste la visualisation qui donne cette impression, mais elle est bien liée à la table Entreprises.

Pour les adresses et les compléments, je ne force juste pas à les rentrer, et je pensais les laisser en NULL dans la base quand ils sont vides. Les champs adresses ont pas mal de champs optionnels puisque les formatages d’adresse sont parfois très différents. Même le champ numéro est optionnel. C’est pareil pour les contacts. Je ne sais pas s’il y a un réel gain à faire comme tu le proposes ? J’ai l’impression que ça complexifierait vachement l’usage pour un gain que je ne comprends pas.

+0 -0

Pour l’ordre, je pensais que l’idée était de le faire par classement alphabétique croissant ou décroissant, et en général on laisse l’utilisateur choisir au moyen d’un bouton ou autre dans l’interface. Mais d’après ce que tu dis, l’ordre des types de produit par exemple est défini une fois pour toutes et est toujours le même, et il ne correspond au classement alphabétique classique ? Si c’est bien le cas, ta proposition initiale était alors correcte, c.-à-d. ajouter un attribut Ordre à la table TypesProduit. Si par contre le classement alphabétique classique est le bon, il suffit, lors de l’interrogation de la base de données avec l’ordre SELECT pour récupérer les données à afficher, de lui adjoindre la clause ORDER BY.

Pour les sites, ce que je dis est que, d’après ta description, il est plus clair et précis de dire qu’une entreprise a plusieurs sites et que chacun d’entre eux a une adresse, plutôt que de dire qu’une entreprise a plusieurs adresses. Il me semble ainsi plus adéquat d’ajouter un attribut EstProduction à une table Sites qu’à une table Adresses. Ceci étant dit, si tu es sûr de ne pas avoir besoin de distinguer un site de son adresse, tu peux décider de les amalgamer, mais cela me paraît… exotique. ^^ On pourrait imaginer par exemple vouloir les attributs ChiffreAffaires et NbEmplois par site plutôt que seulement pour toute l’entreprise, comme illustré ci-dessous.

Sites
Id
IdEntreprise
IdAdresse
ChiffreAffaires
NbEmplois
EstProduction

Pour les produits, si je résume, il y a des catégories de types de produit. Chaque catégorie contient 1 ou plusieurs types de produit, et chaque type de produit ne se trouve que dans une seule catégorie. Ensuite, chaque type de produit est proposé par 1 ou plusieurs entreprises, et pour chaque entreprise, il est produit sur 1 ou plusieurs sites de production (ou adresses). Inversement, chaque entreprise propose 1 ou plusieurs types de produits, et lorsqu’il en propose c’est que 1 ou plusieurs de ses sites (ou adresses) en produit.

Dans l’association entre entreprise et type de produit, tu veux indiquer si le type de produit proposé par l’entreprise est recyclé, réemployé ou biosourcé. Ces attributs sont les mêmes au niveau de l’entreprise, c.-à-d. que p. ex. si une entreprise propose un type de produit biosourcé, alors, pour tous les sites de production de cette entreprise qui fabrique ce type, celui-ci est biosourcé. D’où la table Produits. La modélisation de cette table me semble correcte, le seul bémol pour moi est son intitulé. Je nommerai plutôt cette table EntreprisesProposentTypesProduit par exemple, mais c’est juste une suggestion. En revanche, je comprends ce que tu dis sur les cases à cocher, mais cela n’a pas grand-chose à voir avec les tables de la base de données, c’est plutôt une affaire d’interface. Il se trouve qu’ici elles correspondent, mais même si cette table était éclatée en plusieurs, cela n’empêche absolument pas de proposer dans l’interface une seule ligne de saisie avec des cases à cocher, c’est seulement l’ordre d’insertion sous-jacent qui aurait été différent.

Pour la table LieuProduction, (comme pour la table Produits d’ailleurs), il ne s’agit pas concaténer les clés comme une chaîne de caractères, mais plutôt de déclarer dans la base de données que la clé primaire est multi-attributs ou multicolonnes. On pourrait écrire par exemple (avec PostgreSQL) :

CREATE TABLE LieuProduction (
    IdProduit integer,
    IdAdresse integer,
    PRIMARY KEY (IdProduit, IdAdresse),
    FOREIGN KEY IdProduit REFERENCES Produits,
    FOREIGN KEY IdAdresse REFERENCES Adresses
);

On voit ici que l’on déclare la clé primaire de la table LieuProduction comme étant composée de 2 colonnes. Cela signifie donc qu’il peut y avoir plusieurs lignes avec le même IdProduit, mais pas plusieurs lignes avec le couple {IdProduit, IdAdresse}. Ce n’est pas très grave d’avoir un attribut Id supplémentaire qui serait déclaré comme clé primaire, mais il est simplement inutile. Néanmoins, il peut être rendu obligatoire par les capacités de LibreOffice Base (que je ne connais pas) ; je sais par exemple que l’ORM de Django 2.2 ne prend pas en charge les clés primaires multi-attributs.

Pour la table Biosource, peut-être la renommer MateriauxBiosource ?

Pour les NULL et les champs optionnels, en ce qui concerne les adresses ce que j’ai dit n’est effectivement pas très pertinent, puisque l’on peut mettre à la place du NULL simplement une chaîne de caractères vide. Je voulais simplement attirer l’attention sur le fait que NULL est difficile à gérer, et qu’il faut être prudent lorsqu’on décide de l’utiliser.

+0 -0

Encore merci pour la réponse !

C’est exactement ça pour l’ordre, notamment parce qu’il y a des types qui sont beaucoup moins courants que d’autres (l’isolation, la structure, les meubles sont très courants par exemple, les revêtements de route beaucoup beaucoup moins). Et il s’agit d’une liste avec 50–60 types, de mémoire (et une douzaine de catégories), donc il vaut vraiment mieux que ce soit trié, à mon avis.

En fait, je ne fais vraiment pas la disctinction entre adresse et site, c’est pour ça. Pour moi, un site (de production ou seulement administratif) = 1 adresse, l’intérêt étant de pouvoir appliquer des filtres géographiques et les afficher sur une carte. Si je n’ai pas d’adresse, l’info ne me sert pas, en gros. La proposition pour le chiffre d’affaires et les employés est intéressante, mais ça représente trop de boulot qui ne sera pas à jour, et je ne suis pas sûr de la pertinence pour le public cible qui est un maître d’oeuvre qui cherche des matériaux.
Cependant, si je comprends bien ton point est surtout de séparer les deux parce que c’est "plus propre" comme façon de construire ses données ?

Tu as tout à fait compris pour ce qui est des types et des biosourcés, etc. Effectivement, j’ai sûrement des noms un peu étranges parce que j’ai bossé avec pendant des mois un peu dans mon coin, donc j’ai mes p’tites habitudes même quand elles ne font pas sens. Je vais voler le nom que tu proposes, et je note à l’avenir de faire des noms plus explicites plutôt que trop courts.

Pour ce qui est des cases à cocher, c’était surtout pour le type booléen, mais en relisant ton mmessage effectivement tu parlais juste de vérifier cette logique, tu ne proposais pas de changer ça ^^

Pour ce qui est de la clef primaire de LieuProduction, j’aimerais bien mais effectivement je ne vois pas comment le faire avec LibreOffice, donc tant pis. La base ne sera de toute façon pas si grande, donc je pense qu’on peut se permettre de perdre cette optimisation.

Pour les NULL, je vais avoir besoin d’eux parce qu’il y a énormément d’infos qui ne sont pas faciles à trouver, de question de mises à jour, etc., et je vais forcément avoir des champs vides. Ne serait-ce que la partie lieux de productions, je l’ai préparée mais en réalité c’est pas du tout une info facile à obtenir et c’est donc probable que cette info ne soit pas remplie tout de suite. À voir comment je me démerdrai pour gérer ça dans l’affichage.


J’ai une autre question, d’ailleurs, concernant les relations. Dans LibreOffice, je peux configurer des relations en cas d’actualisation ou de suppression d’une clef primaire. L’actualisation, je ne vois pas bien pourquoi puisque ce n’est pas censé arriver. La suppression, cependant, je peux régler une suppression en cascade des élements dépendants, donc si je supprime une entreprise ça me supprimerait de la base toutes les infos concernant ses adresses et ses produits. Est-ce que vous pensez que c’est une bonne chose de configurer ça ou bien est-ce qu’il est préférable de se garder une sécurité et de rappeler ensuite les lignes orphelines pour les supprimer quand on est sûr de ne pas avoir supprimé une entreprise par erreur ?

+0 -0

Ok pour l’ordre, il est donc lié à la fréquence d’utilisation du type de produit.

Distinguer site et adresse est effectivement « plus propre » à mon avis, cela capture les entités en jeu. Mais d’après ce que tu dis, un site dont on ne connaît pas l’adresse est inutile, et c’est comme s’il n’existait pas en réalité. Dans ce cas, cela fait sens de mélanger les deux. Peut-être du coup renommer la table Adresses en AdressesSites ou quelque chose comme ça ?

La clé superflue pour LieuProduction et quelques autres tables n’aura effectivement pas trop de conséquence (sauf peut-être si tu commences à gérer de très gros volumes de données), donc si tu ne trouves pas comment déclarer une clé multi-colonnes dans LibreOffice Base, ce n’est pas grave.

À propos des NULL, je voudrais juste signaler en prenant exemple sur le complément d’une adresse, qui est une chaîne de caractères, que NULL est différent de la chaîne vide ''. Cela dépend ensuite bien sûr du type de données de l’attribut optionnel que tu gères. Ça ne me semble pas déraisonnable de les utiliser dans ton cas en effet.

Je ne sais pas à quoi fait référence exactement l’actualisation, l’idée est qu’une clé étrangère pourrait changer de valeur ? Par exemple une adresse pourrait être liée une autre entreprise que celle qui s’y trouvait initialement ? Cela pourrait arriver dans le cas de déménagement ou de rachat ou revente d’entreprises peut-être ? Pour la suppression, ça dépend de ton cas d’usage. Dans le doute, je mettrai une protection maximum pour éviter la perte de données (penser aux sauvegardes régulières de la BDD évidemment !), mais cela peut faire sens de paramétrer une suppression automatique. Pour reprendre l’exemple de la suppression d’une entreprise, à priori si on veut la supprimer, il n’y a pas d’intérêt à garder ses adresses de ses sites ainsi que les informations sur les types de produit qu’elle proposait…

+0 -0

Salut,

Oui, je pense que je vais faire un tour pour renommer mes tables et essayer que ce soit un peu plus clair.

Je verrai comment je peux gérer les NULL, je ne sais pas trop comment ça va se passer, et au pire je ne pense pas que ce soit très très difficile de gérer une chaîne vide.

Non, les actualisations et suppressions se font en partant de la clef primaire vers les clefs étrangères. Donc, si tu changes une clef primaire, ça la change dans les clefs étrangères pour garder les correspondances. Je pense que la fonction sert surtout pour la suppression, mais pour l’actualisation je ne vois d’utilité que si la clef primaire n’est pas basée sur un id mais plutôt sur un nom, ce qui ne me semble de toute façon pas une bonne idée.

C’est clair pour la suppression, mais faut que je regarde ce que je peux trouver comme solution pour mettre de la confirmation de suppression ^^

+0 -0

Salut !

Je repasse par ici. Après avoir réfléchi au design et au développement de mon site, je me suis dit que j’allais peut-être fonctionner différemment pour le stockage de mes adresses. L’idée est que les clients, en saisissant l’adresse, aient un champ unique pour rentrer l’adresse, comme on en a l’habitude. On aurait à ce moment-là la résolution d’adresse, afin qu’ils puissent sélectionner la bonne adresse parmi les propositions.

Partant de là, je me dis que le back-end recevrait l’adresse complète, et les coordonnées résolues. Du coup, je me disais que plutôt que de stocker les adresses comme j’avais prévues, je ferais mieux de les stocker en 3 champs :

  • Adresse textuelle : pour l’affichage de l’adresse.
  • Longitude
  • Latitude

Et cela me permettrait d’éviter de faire une résolution d’adresse à chaque fois, en plus de me faciliter les filtres géographiques.

Est-ce que vous me confirmez ça ?

(@sgble je pense que oui, vu que c’est comme ça que tu as fait ^^)

+0 -0
  • Adresse textuelle : pour l’affichage de l’adresse.
  • Longitude
  • Latitude

Et cela me permettrait d’éviter de faire une résolution d’adresse à chaque fois, en plus de me faciliter les filtres géographiques.

Est-ce que vous me confirmez ça ?

(@sgble je pense que oui, vu que c’est comme ça que tu as fait ^^)

Moté

C’est bien l’approche que j’avais utilisée. L’adresse était enregistrée pour l’affichage humain, ainsi qu’un point (lat, lng) qui permet de faire les requêtes en SQL, et évitant aussi de futures résolutions auprès du service de geocoding comme tu le dis si bien.

Merci :) Par contre, tu confirmes que c’est bien un champ unique avec un objet de PostGIS, qui représente directement le point ? Pas besoin de sauvegarder les latitudes et longitudes en deux champs ?

Tiens, j’ai une autre question, est-il intéressant d’enregistrer les dates d’ajout et/ou de dernière mise à jour ?

+0 -0

Merci :) Par contre, tu confirmes que c’est bien un champ unique avec un objet de PostGIS, qui représente directement le point ? Pas besoin de sauvegarder les latitudes et longitudes en deux champs ?

Oui je confirme, il s’agit d’un seul champ, de type geometry, qui encode à la fois la longitude et la latitude.

(je crois que PostGIS propose d’autres types similaires selon le niveau de précision requis (en prenant en compte la courbure de la Terre par exemple), mais le principe reste le même)

Tiens, j’ai une autre question, est-il intéressant d’enregistrer les dates d’ajout et/ou de dernière mise à jour ?

Si tu as besoin de conserver un historique (ce qui peut être pratique), il serait en effet intéressant d’avoir une colonne en plus pour cela.

Hmm, je reviens encore avec une question. Je ne suis plus satisfait avec la manière dont j’ai défini l’organisation de Produits. J’ai en effet décidé de pouvoir relier les lieux de production selon les origines des matériaux :

Du coup, on peut définir des lieux de production pour les panneaux biosourcés, et d’autres lieux pour les panneaux recyclés, etc. Je ne trouve pas que ça colle avec la structure en booléens que j’avais mise plus haut.

Je vois deux solutions :

  • Faire des tables différentes selon que les matériaux soient biosourcés, recyclés, etc.
  • Faire un seul champ texte qui contiendrait l’origine ("biosourcé", "recyclé", "réemploi", "réutilisable")

Vous auriez un avis svp ?

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte