Polymorphisme dans base de donnée?

Polymorphisme dans base de donnée? - SQL/NoSQL - Programmation

Marsh Posté le 04-04-2009 à 14:48:48    

Bonjour,
 
Je me trouve confronté à un problème qui peut être résumé de la manière suivante :
 
J'ai une classe "Boite" qui peut contenir 0-N "Objets", "Objet" étant une interface commune à plusieurs autres classes, ayant comme unique point commun le fait qu'ils peuvent se retrouver dans la même boîte. Boite peut contenir plusieurs instances de la même classe, une seule instance de plusieurs différentes classes ou un mix des deux.
Ceci ne me pose pas grand problèmes tant que je gère ça dans mon programme, par contre je suis un peu emprunté quand il s'agit de concevoir les tables correspondantes : Comment traduire ce lien sachant que je ne peux ni créer une table avec FK objet_1, objet_2 ,... sachant que je ne peux pas savoir combien il y aura d'objets dans chaque boite (à priori moins que 20, mais c'est une estimation à la louche), ni créer une table de jointure boite_id => objet_id , vu que chaque classe aura sa propre table (elles ont toutes des champs distincts).
 
Merci d'avance pour votre aide


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 04-04-2009 à 14:48:48   

Reply

Marsh Posté le 04-04-2009 à 15:55:08    

Ta conception objet est en quel langage ? Parce que j'ai l'impression que tu te poses les problèmes résolus par tous les ORM.


---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 04-04-2009 à 16:05:09    

Rails.

Reply

Marsh Posté le 04-04-2009 à 16:27:39    

Ouais, en fait, c'est galère ton pb.:/


---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 04-04-2009 à 16:51:13    

Le moins pire que j'ai trouvé pour le moment c'est de faire une table de jointure :
 
- boite_id , objet_id, class_name , qui contiendrait par exemple :  
 
 1 , 1, "Chaussure"
 1, 2, "Chaussure"
 1,  1, "Velo"
 ...
 
La 1ère requête sortirait ce qui est dans la boite 1, puis il y aurait une requête par famille d'objets :
 
select * from chaussures where id = 1 or id =2
select * from velos where id = 1
...
 
Vu qu'il n'y aura pas des centaines d'objets par boîte ça reste viable mais je dois admettre que je suis modérément enthousiaste à l'idée de mettre en place un truc du genre :D


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 04-04-2009 à 17:39:40    

Et le topic Rails alors!?  [:thalis]

Reply

Marsh Posté le 04-04-2009 à 20:21:50    

Quoi le topic rails? C'est un problème principalement lié à la conception d'une base de donnée .. Le fait que le langage utilisé pour le site web soit Rails n'a rien à voir avec le problème lui même [:spamafote]


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 05-04-2009 à 11:01:59    

esox_ch a écrit :

Le moins pire que j'ai trouvé pour le moment c'est de faire une table de jointure :
 
- boite_id , objet_id, class_name , qui contiendrait par exemple :  
 
 1 , 1, "Chaussure"
 1, 2, "Chaussure"
 1,  1, "Velo"
 ...


Ca n'a pas l'air d'être une mauvaise idée. :spamafote:
 
Ceci dit, dans ton domaine, les objets sont-ils hétéroclites au point de ne pas pouvoir être représentés dans la même table? Parce que bien entendu, un vélo aura des caractéristiques (nombre de vitesses, diamètre des jantes) qui n'ont rien à voir avec celles d'une chaussure (matériaux intérieur, semelle, taille).  
 
Mais d'un autre côté, quel genre de business a besoin de modéliser dans le détail des choses aussi disparates?
 
Je veux dire, si ton domaine, c'est de fabriquer des boîtes ou des conteneurs pour le transport, ça n'a sans doute pas de sens. Si ton domaine est de vendre de tels articles et qu'il faut bien présenter tout cela au client, est-ce qu'il ne serait pas plus flexible de modéliser Article 1--n Caractéristiques ? Ca fait fourre-tout, mais au moins, tu peux rajouter à l'envi brosse-à-dents, grappin ou antenne satellite sans modifier ton modèle physique.
 
(Bien entendu, le modèle logique ne change pas et tu restes avec ton interface)
 
[:pingouino] ?


---------------
Now Playing: {SYNTAX ERROR AT LINE 1210}
Reply

Marsh Posté le 05-04-2009 à 11:18:39    

Hello,
 
Il s'agit en fait d'une gestion d'inventaire informatique. Je m'explique :
 
Nous avons quelques centaines d'ordinateurs / imprimantes (réseau ou non) /scanners / NAS / ... et nous voulons savoir qui est rattaché à quoi.
Donc nous avons créé un objet "PosteDeTravail" auquel on peut rattacher tous les objets qui le composent : Ordinateur(s), scanner(s), imprimante(s) ,... Avec après la possibilité de voir quelles sont les caractéristiques internes de chaque objet ( de la le besoin d'avoir une table pour chaque objet).
 
Donc on je vais partir sur mon idée de table de jointure .. Merci


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 05-04-2009 à 12:17:28    

esox_ch a écrit :

Avec après la possibilité de voir quelles sont les caractéristiques internes de chaque objet ( de la le besoin d'avoir une table pour chaque objet).


Sans vouloir insister inutilement si ton choix est fait (et je ne peux pas dire que ma proposition soit meilleure que ta solution, bien entendu), cela t'empêche-t-il de modéliser les types de matos et leurs caractéristiques de manière générale (en découplant type de matos et liste de caractéristiques du matos et de ses caractéristiques proprement dites).
 
Au contraire, car tu peux ajouter ou supprimer des caractéristiques et des périphériques sans modifier ton modèle. Vachement plus pratique que d'ajouter iPod et Camera que ton gestionnaire n'aura pas prévu au départ mais que le boss il a ça comme gadget, ou tout autre matos qui ne manquera pas de sortir dans peu de temps (en ce compris les appareils combinés, dieu sait ce qu'on sortira l'année prochaine).
 

Spoiler :

Accessoirement, pour éviter de te limiter, tu pourrais envisager de ne pas avoir de PosteDeTravail du tout. Ca généraliserait et ça ne nuirait en rien à l'ensemble, le poste de travail devenant un matériel comme un autre (rattaché à rien, avec sa classe à lui). Ca permet aussi d'avoir comme poste de travail un laptop ou un desktop ou un netbook qui peuvent être des sous-types distincts de TypeMatériel.


 
Juste mes deux centimes, encore un fois, sans vouloir faire le lourd :o


---------------
Now Playing: {SYNTAX ERROR AT LINE 1210}
Reply

Marsh Posté le 05-04-2009 à 12:17:28   

Reply

Marsh Posté le 05-04-2009 à 12:56:17    

Salut,

 

Si j'ai choisi cette structure, c'est parce que ça me semblait la meilleure, mais vu que rien n'est encore écrit, je peux sans problèmes changer pour quelque chose de mieux.
Par contre je ne comprend pas vraiment ce que tu entends par "découplant type de matos et liste de caractéristiques du matos et de ses caractéristiques proprement dites".
Mon modèle permet aussi de rajouter un IPod si l'envie nous en prend, il "suffit" de rajouter une table IPod, et après on pourrait commencer à ajouter des IPods dans la table de jointure ... Maintenant, comme je l'ai dit, je cherche la meilleure solution donc si tu peux m'expliquer la tienne ça pourrait être intéressant :)

 

Edit : Concernant les Laptops, j'ai prévu un flag boolean dans PosteDeTravail qui renseignerai sur ce point

Message cité 1 fois
Message édité par esox_ch le 05-04-2009 à 12:58:04

---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 05-04-2009 à 14:41:50    

Voilà ce que j'ai fait pour le moment niveau design de la base de donnée :
 
 
 http://hfr-rehost.net/preview/self/pic/4231d375847f76b994245838f6698833fb0106a5.png
 
 
Commentaires/remarques/conseils bien entendu acceptés :)


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 05-04-2009 à 14:57:43    

esox_ch a écrit :

Hello,
 
Il s'agit en fait d'une gestion d'inventaire informatique. Je m'explique :
 
Nous avons quelques centaines d'ordinateurs / imprimantes (réseau ou non) /scanners / NAS / ... et nous voulons savoir qui est rattaché à quoi.
Donc nous avons créé un objet "PosteDeTravail" auquel on peut rattacher tous les objets qui le composent : Ordinateur(s), scanner(s), imprimante(s) ,... Avec après la possibilité de voir quelles sont les caractéristiques internes de chaque objet ( de la le besoin d'avoir une table pour chaque objet).
 
Donc on je vais partir sur mon idée de table de jointure .. Merci


 
Je me suis fait la même réflexion que sircam. A spécialiser les familles de produits ainsi, non seulement tu te fais chier au niveau du SQL parce que tu ne peux pas faire de jointures (et donc des perfs médiocres), mais aussi et surtout tu vas te faire chier au niveau du code, parce que là aussi, tu vas spécialiser les traitements et tu limiteras les possibilités de faire du polymorphisme.
En gros, dans ton modèle, tu as un concept trop large "Objet" et des familles d'objets elles-mêmes trop spécialisées, ce qui me fait souçonner un escamotage du travail d'analyse fonctionnelle.


---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 05-04-2009 à 15:07:31    

Ok mais t'as quelque chose d'autre à me proposer ? :)


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 05-04-2009 à 15:10:54    

esox_ch a écrit :

Ok mais t'as quelque chose d'autre à me proposer ? :)


Une seule table pour tous les composants possibles de ton hardware. Après tu peux créer des "vues" de ton modèle au niveau applicatif qui ne présentent qu'un sous-ensemble de ces données (cf Single Table Inheritance, qui est par ailleurs le modèle d'héritage implémenté par Rails)


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 05-04-2009 à 15:14:18    

D'accord mais donc ça fera une table "composants" avec énormément de colonnes, et la plus part seront pratiquement toujours vides, non?


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 05-04-2009 à 15:17:29    

esox_ch a écrit :

Voilà ce que j'ai fait pour le moment niveau design de la base de donnée :

 


 http://hfr-rehost.net/preview/http [...] 0106a5.png

 


Commentaires/remarques/conseils bien entendu acceptés :)


On voit bien que tous les types de matériel ont chacun model/serial/inventory/invoice. Ca devrait être dans une Classe/table "Materiel/materiel" auquel on ajoute un id de type de matériel (écran, desktop, imprimante, etc) et FK vers la table des utilisateurs attitrés. Un poste consiste donc en un ensemble de matériels attitrés à un utilisateur ou pas (ou à un groupe d'utilisateurs, un groupe pouvant être identifié comme un utilisateur particulier) et affectés à un lieu donné. Déjà, avec "materiel", tu introduis un concept intermédiaire entre "objet" et ses spécialisations et des liens fonctionnels avec les autres entités.
Par ex., certains matériels ont un hostname et une IP qui est liée à plug, je suppose. Bref, je pense qu'il y a pas mal de choses à revoir, et qu'il faut s'occuper des liens fonctionnels entre les différents éléments. Quand ces liens sont bien clairs, avec les FK qui vont bien, cela simplifie énormément les requêtes par la suite et donc accroît les performances et réduit le code.
Ensuite, pour faire du polymorphisme, tu regroupes tes types de materiel dans une seule table materiel_specs, avec des colonnes au nom générique specific1, specific2,... qui sont des varchar (par ex) dans lequel tu mets tout ce qui ne rentre pas ailleurs (graphic card, cpu, etc). tu récupères les infos, et tu les traites en Ruby.
Quand tu vas vouloir récupérer les infos d'une unité centrale, ta requête sera un truc du genre
select specific1 as graphic_card, specific2 as cpu... from materiel_specs avec la jointure immédiate par FK avec materiel.


Message édité par el muchacho le 05-04-2009 à 15:36:32

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 05-04-2009 à 15:37:58    

D'accord effectivement je pourrais mettre model/serial/inventory/invoice dans devices, mais je vois pas en quoi ça résout le problème par rapport à comment je fais le lien vers les spécialisations (à moins de faire ce que Masklin suggère).  
Effectivement les imprimantes et ordinateurs ont une IP (qui peut être nulle ou pas) qui est donc associée (ou pas) à un plug. Cependant je n'ai pas écrit la FK car on considère qu'une work-station est connectée à une prise, et que cette work-station comprend elle-même du matériel, c'est donc le lien workstation-plug qu'il nous semblait utile utiliser et non pas le lien plug-IP (étant donné que workstation peut être un serveur d'impression qui a plusieurs cartes-réseaux auquel se connectent plusieurs imprimantes, ce qui de notre côté est vu comme 1 plug (celui du serveur  vers "l'exterieur" , 1 IP pour le serveur + 1 IP "locale" / imprimante ).

 
Résolu par le edit de el muchacho.
 
 
Edit: Donc le fait d'avoir 1 table avec plein de champs qui n'ont pas grand chose à voir les uns avec les autres est plus "logique" (d'un point de vue conception de base de donnée) que d'avoir la structure que j'ai proposée ?  
Et pourquoi des colonnes avec nom générique et varchar et pas directement le bon nom et type de donnée?


Message édité par esox_ch le 05-04-2009 à 15:40:16

---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 05-04-2009 à 15:44:39    

Citation :

Edit: Donc le fait d'avoir 1 table avec plein de champs qui n'ont pas grand chose à voir les uns avec les autres est plus "logique" (d'un point de vue conception de base de donnée) que d'avoir la structure que j'ai proposée ?  
Et pourquoi des colonnes avec nom générique et varchar et pas directement le bon nom et type de donnée?


Le problème de ne pas faire un materiel générique, c'est que tu ne pourras pas rajouter une famille de matériel nouvelle parce que chaque famille en dur dans ton modèle. Imaginons que vous vouliez rajouter une interface qui puisse permettre de définir un nouveau type de matériel (je sais pas moi, un NAS par ex) avec ses caractéristiques, ça n'entre dans aucune des catégories existantes, donc tu vas devoir te le farcir programmatiquement.
D'où l'idée de faire une abstraction qui permette de décrire le type de matériel.
Par contre, tu ne dois surtout pas faire une table "nom caractéristique/colonne", ou tu réduis tes perfs à zéro.

Message cité 2 fois
Message édité par el muchacho le 05-04-2009 à 17:27:27

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 05-04-2009 à 15:46:16    

el muchacho a écrit :

Le problème de ne pas faire un materiel générique, c'est que tu ne pourras pas rajouter une famille de matériel nouvelle parce que chaque famille en dur dans ton modèle. Imaginons que vous vouliez rajouter une interface qui puisse permettre de définir un nouveau type de matériel (je sais pas moi, un NAS par ex) avec ses caractéristiques, ça n'entre dans aucune des catégories existantes, donc tu vas devoir le faire programmatiquement.


D'un autre côté, ça peut aussi être désirable comme décision, et ça clarifie le système et peut simplifier la maintenance.

Message cité 1 fois
Message édité par masklinn le 05-04-2009 à 15:46:50

---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 05-04-2009 à 15:48:25    

masklinn a écrit :


D'un autre côté, ça peut aussi être désirable comme décision, et ça clarifie le système et peut simplifier la maintenance.


Effectivement, il est parfois désirable de limiter les possibilités. Mais là, tu peux être sûr à 90% que dans les 5 ans à venir, si le truc n'est pas mis au placard, que cette possibilité sera nécessaire. Et comme c'est bloquant (parce qu'une fois le truc en prod, faut pas songer à modifier le modèle derrière)...

Message cité 1 fois
Message édité par el muchacho le 05-04-2009 à 15:52:26

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 05-04-2009 à 15:53:34    

el muchacho a écrit :


Effectivement, il est parfois désirable de limiter les possibilités. Mais là, tu peux être sûr à 90% que dans les 5 ans à venir, si le truc n'est pas mis au placard, que cette possibilité sera nécessaire. Et comme c'est bloquant (parce qu'une fois le truc en prod, faut pas songer à modifier le modèle derrière)...


Je dis pas que le système ne va pas évoluer et que ce besoin ne va pas apparaitre, je dis que dans le cadre de l'évolution de l'application la décision de créer des tables séparées peut néanmoins avoir des avantages.


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 05-04-2009 à 15:59:00    

Oui, mais c'est bloquant pour une éventuelle évolution (non programmatique). Et encore une fois, tu spécialises tes classes, donc tu vas spécialiser ton code pour chaque famille de composant.


---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 05-04-2009 à 16:33:13    

el muchacho a écrit :

Oui, mais c'est bloquant pour une éventuelle évolution (non programmatique).


 [:prozac]

 

C'est ce que je dis depuis tout à l'heure, demander une évolution programmatique en cas d'évolution peut aussi être avantageux [:kiki]


Message édité par masklinn le 05-04-2009 à 16:33:27

---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 05-04-2009 à 16:37:13    

Reply

Marsh Posté le 05-04-2009 à 16:44:18    


La clarté du modèle de DB et des tables, et plus de possibilités dans l'ajout de contraintes (donc une meilleure assurance potentielle sur la cohérence et la complétion de la DB). Tu gagnes en résistance aux erreurs de prod (surtout si la DB est utilisée par plusieurs systèmes indépendants), mais tu paies en coût d'évolution.


Message édité par masklinn le 05-04-2009 à 16:46:23

---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 05-04-2009 à 18:28:22    

Bonjour,
 
Merci de l'attention que vous accordez à mon problème.
Je dois admettre que l'idée de plusieurs tables séparées m'attire plus car c'est un concept avec lequel je suis familier. Maintenant, il y a certains trucs que je ne comprend pas très bien par rapport à ce que tu préconises el muchacho :  
 
- Tu me dis que je devrais faire une seule table qui contiendrait un grand nombre de colonnes avec des noms "génériques" (c_1, c_2, ....) => 1 colonne par attribut d'un objet. Ceci signifie que si j'ai une classe (Comme Printer) qui possède un attribut qui lui est propre (comme "color" ), ce champs sera "null" pour dans la majorité des cas. Non? C'est donc quelque chose qui n'est pas en soit gérant pour la bdd et qui est usuel ?
- Ensuite, où est-ce que je fais la relation "c_1" => "color", si je ne dois pas faire de table qui fait ce lien? Je crée une vue qui ferait elle la "traduction" ? Ce qui signifierai qu'en cas d'ajout de nouveau type de matériel je devrais rajouter une colonne c_X et une vue correspondante?  
 
Je suis désolé si mes questions sont à côté de la plaque mais comme je l'ai dit auparavant, c'est une structure avec laquelle je ne suis absolument pas familier et du coups je ne comprend pas quels sont les avantages qu'elle présente, et ce qu'elle implique au moment de la mise en place.


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 05-04-2009 à 18:34:41    

esox_ch a écrit :

Je dois admettre que l'idée de plusieurs tables séparées m'attire plus car c'est un concept avec lequel je suis familier.


Dans l'idéal, pour avoir les avantages & inconvénients de chaque solution tu devrais checker les sections correspondantes de PoEAA: Single Table Inheritance (supportée nativement par Rails), Class Table Inheritance, Concrete Table Inheritance; et Inheritance Mapper en structure.


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 06-04-2009 à 00:20:10    

esox_ch a écrit :

Bonjour,
Maintenant, il y a certains trucs que je ne comprend pas très bien par rapport à ce que tu préconises el muchacho :

 

- Tu me dis que je devrais faire une seule table qui contiendrait un grand nombre de colonnes avec des noms "génériques" (c_1, c_2, ....) => 1 colonne par attribut d'un objet. Ceci signifie que si j'ai une classe (Comme Printer) qui possède un attribut qui lui est propre (comme "color" ), ce champs sera "null" pour dans la majorité des cas. Non? C'est donc quelque chose qui n'est pas en soit gérant pour la bdd et qui est usuel ?


Oui, c'est bien ça. En général, avec une dizaine de colonnes, c'est suffisant dans la plupart des cas, mais bien sûr, l'espace avec les NULL est perdu.Une autre manière est de rassembler plusieurs champs dans un varchar que tu décodes ensuite après l'avoir lu (séparés par un caractère particulier comme | par ex.).C'est moins pratique mais plus économique. Par ailleurs, certaines bases (Oracle, Postgres) permettent de mettre une structure dans un seul champs de BD. C'est pratique, mais en général non portable. Les liens sur l'héritage de Masklinn sont intéressants, c'est les seules manières de faire de l'héritage en BD (le fameux object-relational "impedance mismatch" ).Mais dans ce cas le mieux est encore d'exploiter la possibilioté de faire de l'héritage qu'offre ces bases. Après, je sais pas si Rails exploite cela mais c'est peu probable que ça le soit nativement.

Citation :


- Ensuite, où est-ce que je fais la relation "c_1" => "color", si je ne dois pas faire de table qui fait ce lien? Je crée une vue qui ferait elle la "traduction" ? Ce qui signifierai qu'en cas d'ajout de nouveau type de matériel je devrais rajouter une colonne c_X et une vue correspondante?


Le lien est fait soit par la requête d'accès
select c_1 as color, c_2 as cue from specific where ...
soit au niveau de ton code Ruby, puisque tu connais le type de composant que tu cherches. Soit, si tu veux vraiment tout en BD, dans une table qui décrit les colonnes en fonction du type de composant.
Mais là, tu dois faire 2 requêtes à chaque fois: une pour la description des champs de la classe, l'autre pour récupérer les valeurs. C'est un peu l'usine à gaz et pas franchement optimal coté perfs.


Message édité par el muchacho le 06-04-2009 à 00:24:29

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 06-04-2009 à 08:42:58    

Ok,
 
Effectivement ça me semble assez compliqué et à vrai dire je ne vois pas très bien ce que j'ai à y gagner d'un point de vue de l'évolution car :
 
Avec "ma" méthode : Si un nouveau periférique devait être nécessaire, je devrais créer la classe+table qui va bien et c'est à peu près tout il me semble.
Avec "ta" méthode : Il faudrait que j'ajoute d'autres champs "génériques" et que j'écrive quand même un nouveau modèle qui effectuerait les requêtes.
 
Donc il me semble que dans les 2 cas, il faudrait mettre les mains au code.
 
Sinon j'ai commandé le livre que tu m'as conseillé masklinn, il a l'air pas mal du tout (et assez simple à comprendre :D )


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 06-04-2009 à 09:28:10    

esox_ch a écrit :

Sinon j'ai commandé le livre que tu m'as conseillé masklinn, il a l'air pas mal du tout (et assez simple à comprendre :D )


:)
 
Si t'as pas, je recommande également Refactoring, par Fowler


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 06-04-2009 à 16:17:17    

Je prend le sujet en route car c'est le problème que j'ai rencontré, évoqué dans la cat' Rails.
 
Par rapport aux derniers posts sur ce thread, je suis finalement parti sur un modèle en STI, surtout parce que c'est dispo sous Rails. En gros, j'ai 3 ou 4 modèles hérités d'une superclasse, chacun à une valeur d'un type différent. Alors forcément, tu te retrouve avec beaucoup de champs inutiles selon les entrées, mais l'avantage de la solution, c'est que tu peux y apposer certains traitements génériques sans trop de soucis, en jouant avec les alias d'attributs et de méthodes par rapport à ta superclasse notamment.

Reply

Marsh Posté le 06-04-2009 à 19:35:46    

esox_ch a écrit :

Ok,

 

Effectivement ça me semble assez compliqué et à vrai dire je ne vois pas très bien ce que j'ai à y gagner d'un point de vue de l'évolution car :

 

Avec "ma" méthode : Si un nouveau periférique devait être nécessaire, je devrais créer la classe+table qui va bien et c'est à peu près tout il me semble.
Avec "ta" méthode : Il faudrait que j'ajoute d'autres champs "génériques" et que j'écrive quand même un nouveau modèle qui effectuerait les requêtes.

 

Donc il me semble que dans les 2 cas, il faudrait mettre les mains au code.

 

Sinon j'ai commandé le livre que tu m'as conseillé masklinn, il a l'air pas mal du tout (et assez simple à comprendre :D )


Tu devras aussi développer une IHM pour ce nouveau périphérique.
Le mieux est de voir si tu peux faire une IHM la plus générique possible pour les certains périphériques.

 

Dans le cas générique que je t'ai décrit, tu construis ta requête SQL en fonction d'un mapping colonne/nom que tu peux lire dans un fichier par ex. L'évolution est alors triviale.

 

Sinon STI, c'est pas scalable, à éviter.


Message édité par el muchacho le 06-04-2009 à 19:41:12
Reply

Marsh Posté le 10-04-2009 à 10:30:46    

J'espère ne pas répéter une solution proposée par les autres intervenants; je n'ai relu ce topic que de manière superficielle.

esox_ch a écrit :

Par contre je ne comprend pas vraiment ce que tu entends par "découplant type de matos et liste de caractéristiques du matos et de ses caractéristiques proprement dites".


Type de matos : iPod, imprimante, NAS, latop, ...
Caractéristiques (par exemple) : Pour une imprimante, marque, modèle, numéro de série, interface (USB, série, ...), type (jet d'encre, laser, ...). Pour un iPod : numéro de série, date d'achat, ...
 
À ce stade là, tu n'as pas encore encodé la moindre imprimante ou le moindre iPod. Tu n'as fait que capturer, dans la DB, ces entités encore abstraites. Remarque que tu peux déjà, dans ta couche applicative, ajouter un type de matos (p.e., aujourd'hui, on répertorie les joysticks) et des caractéristiques (on veut à présent connaître la capacité papier des imprimantes).
 
Tu passes ensuite au matos concret : telle imprimante, numéro de série 234E32, marque HP, modèle SuperJet, type inkjet, achetée 2005-01-05.
 
Pas le moindre champs "date d'achat" ou "numéro de série" : ce ne sont que des caractéristiques d'un matos donné.
 
C'est assez infâme car c'est très abstrait, avec des paires clé-valeur partout, mais tu as zéro maintenance DB et applicative. Un nouveau type de matos s'ajoute dans l'appli, de nouvelles caractéristiques aussi.
 

esox_ch a écrit :


Mon modèle permet aussi de rajouter un IPod si l'envie nous en prend, il "suffit" de rajouter une table IPod,


Faut pas rêver : si ton système doit durer, ça sera vite intenable, mais en contrepartie, ça aura le mérite de modérer la prolifération d'entités (hey, on stockerait bien les machines à café, et les plantes aussi). [:jar jar]


---------------
Now Playing: {SYNTAX ERROR AT LINE 1210}
Reply

Marsh Posté le 10-04-2009 à 10:35:48    

N.B. Ce que je raconte peut être traduit par des colonnes génériques c1, c2, c3, ... mais on peut aussi créer une relation 1--n à la place, avec jointure à la clé.
 
Contrairement à ce que propose el muchacho, je ne pensais pas à un truc comme "c_1 as color, c_2 as cue", car ça ne fait que reporter le problème de maintenance de la DB vers la couche persistance.
 
À la place, je stockerais "color" et "cue" comme données. [:mlc]


---------------
Now Playing: {SYNTAX ERROR AT LINE 1210}
Reply

Marsh Posté le 10-04-2009 à 10:48:43    

el muchacho a écrit :


....
Par contre, tu ne dois surtout pas faire une table "nom caractéristique/colonne", ou tu réduis tes perfs à zéro.


 

sircam a écrit :

..
Contrairement à ce que propose el muchacho, je ne pensais pas à un truc comme "c_1 as color, c_2 as cue", car ça ne fait que reporter le problème de maintenance de la DB vers la couche persistance.
 
À la place, je stockerais "color" et "cue" comme données. [:mlc]


 
Ce n'est pas justement ce que el muchacho disait de ne pas faire car ça allait défoncer mes performances?  
Sinon là j'attends toujours de recevoir le livre sur les pattern que j'ai commandé sur amazon  :bounce: Peut-être qu'après j'y verrai un peu plus clair


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 10-04-2009 à 11:38:58    

esox_ch a écrit :

Ce n'est pas justement ce que el muchacho disait de ne pas faire car ça allait défoncer mes performances?


Premature optimization is t3h r00t of all evil. [:pingouino]  
 

Spoiler :

Oui, je sais, l'adage ne trouve pas forcément à s'appliquer à tous les cas de figure.  [:dawa]


Ceci dit, quelque soit le modèle, rien ne t'empêche de générer des données à blanc en volume représentatif et de faire quelques queries pour voir si c'est supportable.


---------------
Now Playing: {SYNTAX ERROR AT LINE 1210}
Reply

Marsh Posté le 10-04-2009 à 14:33:35    

Salut,
 
Je suis ton sujet depuis le début, et ça me rappelle une problèmatique à laquelle j'avais proposé une solution ici :  
 
http://forum.hardware.fr/hfr/Progr [...] 9860_1.htm
 
Cette solution est un "mélange" des 2 idées que j'ai cru dégager des différentes réponses, à savoir :  
- ne pas créer inutilement les champs qui seront de toute façon à NULL (on n'a pas besoin de prévoir le champ "page par minute" pour un i-Pod),
- ne pas alourdir la table qui va contenir les informations nécessaires avec le type d'information, puisqu'il s'agit d'un lien vers une table de référence.
 
C'est assez souple à maintenir, si tu as besoin d'une nouvelle caractéristique, il suffit de l'ajouter dans la table et tu peux t'en servir rapidement, et niveau performance, j'ai déjà eu à travailler avec ce système sous SQL 2005 avec plusieurs millions d'objets ayant chacun 0-N caractéristiques, ça répond plutôt bien avec les bons index.
 
Bon courage,

Reply

Marsh Posté le 10-04-2009 à 19:41:07    

Ce modèle est celui utilisé par EZPublish (CMS en PHP) et il offre des performances effroyables pour ce CMS en tout cas...

Reply

Marsh Posté le 18-04-2009 à 11:03:53    

Bonjour,

 

Je pense que finalement je vais utiliser un modèle supporté par Rails qu'ils appellent "polymorphic associations". En gros le principe est que rails crée 2 champs "resource_type" et "resource_id" dans la classe mère (ici device) et utilise une FK sur ces champs pour linker les petits.
Voilà le SQL généré par Rails pour la création de table

 
Code :
  1. CREATE TABLE `screens` (`id` int(11) DEFAULT NULL AUTO_INCREMENT PRIMARY KEY, `model` varchar(255), `serial` varchar(255), `dvi` tinyint(1), `remarks` text, `created_at` datetime, `updated_at` datetime) ENGINE=InnoDB
  2. CREATE UNIQUE INDEX `index_screens_on_serial` ON `screens` (`serial`)
  3.  
  4. CREATE TABLE `scanners` (`id` int(11) DEFAULT NULL AUTO_INCREMENT PRIMARY KEY, `model` varchar(255), `serial` varchar(255), `remarks` text, `created_at` datetime, `updated_at` datetime) ENGINE=InnoDB
  5. CREATE UNIQUE INDEX `index_scanners_on_serial` ON `scanners` (`serial`)
  6.  
  7. CREATE TABLE `printers` (`id` int(11) DEFAULT NULL AUTO_INCREMENT PRIMARY KEY, `model` varchar(255), `color` tinyint(1), `ip` varchar(255), `hostname` varchar(255), `serial` varchar(255), `remarks` text, `created_at` datetime, `updated_at` datetime) ENGINE=InnoDB
  8. CREATE UNIQUE INDEX `index_printers_on_serial` ON `printers` (`serial`)
  9.  
  10. CREATE TABLE `desktops` (`id` int(11) DEFAULT NULL AUTO_INCREMENT PRIMARY KEY, `model` varchar(255), `serial` varchar(255), `os_id` int(11), `cpu` varchar(255), `ram` varchar(255), `graphic_card` varchar(255), `extensions` varchar(255), `hdd` varchar(255), `motherboard` varchar(255), `mac_addr` varchar(255), `ip` varchar(255), `hostname` varchar(255), `remarks` text, `created_at` datetime, `updated_at` datetime) ENGINE=InnoDB
  11. CREATE UNIQUE INDEX `index_desktops_on_serial` ON `desktops` (`serial`)
  12.  
  13. CREATE TABLE `devices` (`id` int(11) DEFAULT NULL AUTO_INCREMENT PRIMARY KEY, `workstation_id` int(11), `resource_id` int(11), `resource_type` varchar(255), `created_at` datetime, `updated_at` datetime) ENGINE=InnoDB
  14. CREATE INDEX `index_devices_on_workstation_id` ON `devices` (`workstation_id`)
  15.  
  16. CREATE TABLE `workstations` (`id` int(11) DEFAULT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar(255), `created_at` datetime, `updated_at` datetime) ENGINE=InnoDB
  17. CREATE UNIQUE INDEX `index_workstations_on_name` ON `workstations` (`name`)
 

J'ai peuplé la base de donnée d'un volume représentatif de ce qu'on risque d'avoir normalement. 500 Workstations, 1000 écrans, 100 scanners & imprimantes

 

J'ai effectué 2 requêtes qui me semble être particulièrement exigeantes en termes de resources.
La 1ère revient à demander la liste de l'équipement connecté à une workstation à la manière "Rails"

 
Code :
  1. >> ActiveRecord::Base.logger.silence do
  2. ?>    elapsed_time = Benchmark.realtime do
  3. ?>       Workstation.find_by_name("DZDHAY2" ).devices.each do |device|
  4. ?>          p device.resource.class
  5. ?>       end
  6. ?>    end
  7. >> end
  8.  
  9. Screen(id: integer, model: string, serial: string, dvi: boolean, remarks: text, created_at: datetime, updated_at: datetime)
  10. Screen(id: integer, model: string, serial: string, dvi: boolean, remarks: text, created_at: datetime, updated_at: datetime)
  11. Desktop(id: integer, model: string, serial: string, os_id: integer, cpu: string, ram: string, graphic_card: string, extensions: string, hdd: string, motherboard: string, mac_addr: string, ip: string, hostname: string, remarks: text, created_at: datetime, updated_at: datetime)
  12. => 0.00327801704406738


Donc 3.2 ms  => à priori ça devrait pas poser de soucis

 

Sans surprises ça se traduit en 1 select / table fille + quelques "SHOW TABLES" dont je ne connais pas la raison. Là je suis plus surpris c'est qu'il ne fait pas de jointure entre workstations et devices. Mais la traite "en interne", ce qui lui fait probablement perdre du temps :heink:
(Remarque : Les logs ci-dessous ont été obtenus en relançant la requête sans le logger.silence et Beanchmark.realtime

 
Code :
  1. Workstation LOAD (0.1ms)   SELECT * FROM `workstations` WHERE (`workstations`.`name` = 'DZDHAY2') LIMIT 1
  2.  Device LOAD (0.1ms)   SELECT * FROM `devices` WHERE (`devices`.workstation_id = 12)
  3.  Screen LOAD (0.0ms)   SELECT * FROM `screens` WHERE (`screens`.`id` = 792)
  4.  SQL (0.2ms)   SHOW TABLES
  5.  Screen LOAD (0.1ms)   SELECT * FROM `screens` WHERE (`screens`.`id` = 834)
  6.  SQL (0.1ms)   SHOW TABLES
  7.  Desktop LOAD (0.1ms)   SELECT * FROM `desktops` WHERE (`desktops`.`id` = 489)
  8.  SQL (0.2ms)   SHOW TABLES
 


La deuxième requête consiste à chercher quel à quelle Workstation est connecté un device donné (un écran dont le connais le serial pour mon exemple)

Code :
  1. ?> ActiveRecord::Base.logger.silence do
  2. ?>  elapsed_time = Benchmark.realtime do
  3. ?> p Screen.find_by_serial("692AVJXZ" ).device.workstation.name
  4. >> end
  5. >> end
  6. "2VD7VJ6"
  7. => 0.0013880729675293


Niveau simplicité du code c'est dur de faire beaucoup mieux je pense. Là encore 1 ms => ça devrait jouer..

 

Et la requête qui va avec ( plus de show table là par contre)

Code :
  1. Screen LOAD (0.1ms)   SELECT * FROM `screens` WHERE (`screens`.`serial` = '692AVJXZ') LIMIT 1
  2.  Device LOAD (0.0ms)   SELECT * FROM `devices` WHERE (`devices`.resource_id = 65 AND `devices`.resource_type = 'Screen') LIMIT 1
  3.  Workstation LOAD (0.0ms)   SELECT * FROM `workstations` WHERE (`workstations`.`id` = 324)
 

Même constat qu'avant, il semble ne pas faire de jointures entre les différentes tables...

 

Je précise que le script que j'ai utilisé pour peupler la base de donnée a généré des données "cohérentes" avec ce qu'il y aura dans ces champs ( = des string random de taille comparable à celles qui seront entrées)

 

Selon moi ça devrait jouer, vous voyez des soucis ?
Niveau maintenance, effectivement ce ne sera pas totalement indolore, mais c'est assez simplifié par les scripts de migration & generateurs de Rails, donc ça me va.

 

Merci à tous


Message édité par esox_ch le 18-04-2009 à 11:14:40

---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed