Probleme de listing alphabétique et LIMIT avec jointures 1:n

Probleme de listing alphabétique et LIMIT avec jointures 1:n - SQL/NoSQL - Programmation

Marsh Posté le 23-12-2004 à 22:07:04    

Je vais essayer d'exposer simplement le probleme en retirant toute table ou colonne non concernée.
Pour un site de jeux videos, j'ai 3 table, une table t_jeu contenant les infos sur le jeu, une table t_nom liée (n:1) contenant les noms du jeu (nom japonais, ricain, francais...), elle meme liée à une table t_zone indiquant la zone (toutes, us, europe, fr, de, es, jp...) pour laquelle ce nom est valable
 

Code :
  1. t_jeu                      t_nom                   t_zone
  2. --------------------     ---------------------     ---------------------
  3. | jeu_id           |     | nom_id            |     | zne_id            |
  4. --------------------     | nom_nom           |     | zne_code          |
  5.                          | jeu_id            |     ---------------------
  6.                          | zne_id            |
  7.                          ---------------------


 
Chaque jeu peut donc avoir plusieurs nom, chacun etant valable pour une "zone" (us, fr, de, en, jp, + eu quand le nom est pour l'europe, et all pour le monde)
Forcément chaque jeu n'a pas forcément un nom pour chaque zone (un seul si c'est "all", il peut avoir un nom us, un eu et un jp...)
 
Le site étant multilangue, il serait bon d'avoir un listing dans la langue du visiteur. La problématique est donc la suivante :
 
Parvenir à lister par pages de 20, par exemple, en connaissant le nombre de pages, tous les jeux en donant priorité au titre de la langue de la personne, suivi du titre us (si il n'y a pas de titre pour sa langue), suivi du titre japonais (si il n'y a pas non plus de titre us, i.e. un jeu sorti seulement au japon par exemple)
 
Le probleme est multiple, récupérer un titre "alternatif" au titre de la langue du visiteur si celui ci n'existe pas, tout en pouvant utiliser le "LIMIT 20", tout en affichant  dans l'ordre alphabétique.
Je ne pense pas que ce soit faisable directement meme avec une requete complexe (ceci dit je n'ai pas une grande expérience des jointures) et sans sous requetes (mon hébergeur n'étant pas encore passé a mysql 4.1.x, bien que en pratique tous les serveurs seront au moins en 4.1.x d'ici à ce que le site soit prêt, donc une solution avec sous requetes est acceptable)
 
J'ai bien des solutions alternatives, mais je préfererais avoir des avis sur la solution la plus optimale.
 
- Je pourrais éventuellement imposer de saisir pour chaque jeu un titre pour chaque zone possible, en mettant le titre us, par exemple, pour les zones n'ayant aucun titre affecté, avec une colonne booléenne déterminant si on doit afficher ou non ce nom alternatif sur le site (en n'affichant pas ceux qui sont mis la pour "boucher". Cette solution est "sale", ca impose bcp de redondence, bref je pense qu'il y a bien mieu, voire qu'il n'y a pas pire
 
- Le site étant destiné à etre au maximum en 4 langues (fr, en, de, es), Je pourrais, en plus de garder la table t_noms (permettant aussi de saisir des noms alternatifs, comme des noms informels, des abrégé communément utilisés...), gérer les noms succeptibles d'etres triés (us, fr, de, es) dans la table t_jeu. Dans ce cas aussi il faut combler les trous si on veut pouvoir utiliser LIMIT et ORDER BY comme il faut (en assignant a tous le nom jp si il n'y a que lui par exemple). Ca n'a pour mérite que de réduire à 4 le nombre de zones dont on doit mettre systématiquement le titre, ce n'est pas beaucoup mieu
 
- Un compromis serait de lister les jeux par noms francais pour les francais, et par nom americain pour les autres, ce qui réduit a fr, us et jp les colonnes à garder dans la table t_jeu, mais la aussi avec un max de redondence (un jeu sorti uniquement au japon aura son nom répété dans les colonnes us et fr, un jeu ayant le meme nom dans le monde entier le vera répété dans les 3 colonnes), mais pour l'instant (étant rouillé et ayant un peu de mal à me concentrer :p), je ne vois pas grand chose de plus "propre" répondant à mes attentes.
Pour que ce soit fonctionnel, il faudrait que ces 3 colonnes ne servent que pour le listing, l'affichage des noms étant toujours géré uniquement dans t_nom (sinon le fait de répéter le nom japonais dans la colonne fr donnera sur le site l'affichage du nom japonais avec (fr) entre parentheses en plus du nom japonais "normal" ). Et ca donne 3 colonnes de redondence pure...  
 
 
Je suppose que j'ai sorti pas mal d'abérations, et si quelqu'un a une facon plus optimale de gérer au mieu la chose, je suis preneur :)
Encore une fois, il y a peut être de quoi avec des sous requetes plus complexe, mais je n'en ai pas l'habitude et me demande si il y a moyen de garder l'utilisation de l'ordre et de la limite avec, sans faire de select * suivi d'un traitement php bien sur.
Le probleme principal reste à mon avis le ORDER BY quand plusieurs colonnes peuvent etre impliquées, qui impose de toutes les remplir pour pouvoir avoir un bon ordre alphabétique à partir d'une seule colonne

Reply

Marsh Posté le 23-12-2004 à 22:07:04   

Reply

Marsh Posté le 23-12-2004 à 23:00:35    

Tu dois pouvoir le faire dans une requête je pense ...
 
Tu pars de la table t_jeu et tu fais autant de jointures (externes, LEFT JOIN) avec t_nom qu'il y a de langues différentes. Ensuite tu fais des CASE pour pouvoir récupérer le nom dans la langue prioritaire.
 
Exemple avec juste 2 langues (FR, US) :
 

Code :
  1. SELECT CASE n1.nom_nom
  2.        WHEN NULL THEN n2.nom_nom
  3.        ELSE n1.nom_nom
  4. FROM t_jeu j
  5. LEFT JOIN t_nom n1 ON j.jeu_id = n1.jeu_id
  6. LEFT JOIN t_nom n2 ON j.jeu_id = n2.jeu_id
  7. WHERE n1.zne_id = '$fr_zne_id
  8. AND   n2.zne_id = '$us_zne_id;


 
Essaye déjà ça, si ça marche, tu peux ajouter plus de jointures pour les autres langues et imbriquer les CASE WHEN (à tester).
 
Et si tu as besoin de trier / limiter et que ça marche pas facilement, mets le résultat de ce SELECT dans une table temporaire, que tu pourras, elle, facilement trier et limiter.


Message édité par Beegee le 23-12-2004 à 23:01:45
Reply

Marsh Posté le 23-12-2004 à 23:17:23    

bah non justement, la ca fait faire un select * justement, et la table étant destinée a avoir plus de 20000 entrées, je te dis pas le cout... le but est de sortir juste le nombre d'éléments à afficher, et surtout, ce qui est dur, le tri alphabétique

Reply

Marsh Posté le 24-12-2004 à 07:24:46    

De toute façon, si tu as un tri à faire, en interne, mySql va se taper les 20.000 lignes, donc je vois pas ce que tu veux améliorer.
 
edit: une autre solution, c'est d'avoir un petit script que tu lances quand tu as fait une mise à jour d'au moins une des tables citées, et qui prépare des tables pour les listings. Comme ça tu le fais une fois seulement, et ensuite, quand le site est à nouveau dispo en ligne, les SELECT peuvent se faire directement dans ces tables de listings fraîchement triées ...


Message édité par Beegee le 24-12-2004 à 07:27:14
Reply

Marsh Posté le 24-12-2004 à 10:15:30    

tu ne sembles pas comprendre le fait que faire un select * sur une telle table est une abération. Pour la fonction de tri, en ayant un index sur la colonne à trier ca ne repasse pas tout, en tout cas c'est bien moins lent qu'un select *
 
quant à mettre à jour une table de tri, c'est quelque chose de tré lourd (surtout si on doit mettre à jour l'indice de tri de chaque ligne), sur 30000 enregistrements.
 
La solution la moins couteuse en perf semble etre, pour l'instant, et sans pouvoir utiliser les sous-requetes, de mettre une colonne avec le nom à utiliser lors du tri fr et une colonne avec le nom à utiliser lors du tri anglais directement dans la table t_jeu. On y perd surtout en taille, surtout si j'utilise des char(100) pour ne pas avoir de varchar, ce qui fait 100octets * 2 colonnes * 30000, soit  dans les 6Mo, ce qui n'est pas énorme mais pas négligeable, mais probablement pas si andicapant que ca, ces colonnes n'étant utilisées que pour le tri, et donc pas pour l'affichage d'une fiche jeu ou toute page autre que le listing

Reply

Marsh Posté le 24-12-2004 à 10:30:34    

Galak_ a écrit :


La solution la moins couteuse en perf semble etre, pour l'instant, et sans pouvoir utiliser les sous-requetes, de mettre une colonne avec le nom à utiliser lors du tri fr et une colonne avec le nom à utiliser lors du tri anglais directement dans la table t_jeu. On y perd surtout en taille, surtout si j'utilise des char(100) pour ne pas avoir de varchar, ce qui fait 100octets * 2 colonnes * 30000, soit  dans les 6Mo, ce qui n'est pas énorme mais pas négligeable, mais probablement pas si andicapant que ca, ces colonnes n'étant utilisées que pour le tri, et donc pas pour l'affichage d'une fiche jeu ou toute page autre que le listing


 
Ca revient à ce que je dis dans l'edit au dessus : tu prépares les données pour les listings.
 
Si tu fais ça, je te conseille de le faire par un script prévu pour, que tu as juste à lancer quand tu mets à jour les données. Et de préférence que ça produise le résultat dans de nouvelles tables, histoire de garder un schéma propre, et à côté des tables redondantes uniquement pour améliorer les perfs lors des listings.

Reply

Marsh Posté le 24-12-2004 à 10:53:02    

oh bah c'est simple hein, à la saisie d'un jeu je prend l'ordre de priorité des titres pour un affichage en anglais (us, all, en, jp, eu, fr...) par exemple, et pour un affichage en francais (fr, eu, all, en, us, jp... enfin à définir), et je met le premier qui est défini dans la colonne correspondante. Idem lors de l'ajout ou la modif d'un titre. Le probleme de cette méthode, outre l'alourdissement d'une table, est l'obligation de rajouter 2 colonnes si je décide de faire un listing préci aussi puor l'allemand et l'espagnol (ce serait bien d'avoir ca aussi pour l'allemand, car bcp de joueurs allemands, et bcp de titres localisés), ce qui alourdi d'autant la base.
Bref ce n'est pas extremement évolutif comme méthode, mais par contre je peu toujours partir dessus (le site se remplissant petit à petit) et mettre au point une autreméthode par la suite, la migration des tables n'étant pas un énorme probleme vu que la plupart des requetes sur le nom se feront directement dans la table t_nom

Reply

Marsh Posté le 24-12-2004 à 10:59:44    

C'est pour ça que je te dis de faire un petit script PHP tout simple qui va te créer comme un grand une table jeu_listing__fr_t qui contiendra par exemple jeu_id et jeu_nom (en fr, us, etc. suivant la priorité). Ca te permet de ne pas dupliquer les infos au moment où tu mets à jour les jeux (c'est le script qui le fait ensuite).

Reply

Marsh Posté le 24-12-2004 à 11:21:33    

bah, tu voulais que je le fasse comment... je vais pas faire tous les test directement en SQL hein :)
 
sinon apres se pose effectivement la question de définir si je met ces colonnes directement dans la table t_jeu ou si je crée une table t_listing qui contient l'id de chaque jeu et une colonne par listing (us et fr au départ). Ca n'a pas vraiment d'interet, sauf pour un select * (dont je devrais me passer en principe) de séparer cette table pour une relation 1:1, c'est juste plus "propre" visuellement et conceptuellement parlant, alors que ca impose de faire un join (toujours plus lourd qu'une requete identique dans la meme base) et j'ai entendu dire qu'en théorie le nombre de colonne ne pénalise pas un SELECT (qui ne se fait pas sur ce surplus de colonnes)

Reply

Marsh Posté le 24-12-2004 à 11:44:08    

C'est toi qui vois, mais je te conseille de charger les données de listings par un script, histoire de ne pas avoir à te rappeler quoi faire à chaque fois que tu crées un nouveau jeu ou que tu remplis un nouveau nom.

Reply

Marsh Posté le 24-12-2004 à 11:44:08   

Reply

Marsh Posté le 24-12-2004 à 11:58:14    

bah, le site est en php, donc ce que tu dis n'a pas vraiment de sens, ce sera forcément géré en php... apres tu veux peut etre dire "faire un fonction" au lieu de "faire un script", qu'on appelle sur chaque fois, mais bon ca c'est une évidence
 
Le coté php ne me pose aucun probleme, c'est juste le manque d'expérience avec certaines notions de mysql qui peut me freiner (les join et les sous requetes).


Message édité par Galak_ le 24-12-2004 à 12:00:11
Reply

Marsh Posté le 24-12-2004 à 12:05:08    

Ouaip, on va pas jouer sur les mots, en gros il te faut une petite partie  administration du site, dans laquelle en cliquant sur un lien, ça appelle un script php qui génère les infos de listings.
 
En pratique, c'est pas très compliqué, tu fais le SELECT suivant (exemple donné pour pour fr, us et jap uniquement) :
 

Code :
  1. SELECT n1.nom_nom as nom_fr,
  2.        n1.nom_nom as nom_us,
  3.        n3.nom_nom as nom_jap
  4. FROM t_jeu j
  5. LEFT JOIN t_nom n1 ON j.jeu_id = n1.jeu_id
  6. LEFT JOIN t_nom n2 ON j.jeu_id = n2.jeu_id
  7. LEFT JOIN t_nom n3 ON j.jeu_id = n3.jeu_id
  8. WHERE n1.zne_id = '$fr_zne_id
  9. AND   n2.zne_id = '$us_zne_id
  10. AND   n3.zne_id = '$jap_zne_id;


 
Et quand tu le parses en php, tu décides de quel nom doit aller dans le listing français, quel nom dans le listing us, etc. et tu remplis les infos de listings.
 
Même avec 30.000 jeux, ça devrait tourner assez vite (à mon avis moins de 5 secondes si le serveur suit), et de toute façon, tu ne lanceras ce process que quand tu mettras à jour les infos de jeux, donc rarement (et de toute façon dans cette période, le site est pas dispo correctement).

Reply

Marsh Posté le 24-12-2004 à 12:32:06    

visiblement tu captes pas du tout le truc, pour la lecture suffit de faire un join sur l'id du jeu, pas besoin de chercher plus loin, et limit et order peuvent etre utilisés, et pour la maj ce sera une cuisine interne qui se basera aussi, en premier lieu, sur l'id du jeu, de facon a retourner autant de lignes pour un jeu donné qu'il y a de noms pour ce jeu. a partir de la une simple boucle retournera tous les noms saisis pour ce jeu.
Le coté admin ne me pose aucun probleme, le probleme est plus, encore une fois, d'avoir une partie site (donc affichage) a la fois ergonomique et légere (car possibilité de pas mal de visites), et la je cherche à voir si il y a mieu que ma solution actuelle, n'ayant pas une grande expérience des jointures et sous requetes (et donc ne sachant pas avec précision si tel ou tel truc est réalisable et le cout que ca a), d'autant que d'ici à ce que le site soit en ligne l'hébergeur sera passé sous mysql 4.1.7, donc avec gestion des sous requetes (et parait il une vitesse accrue).
 
En définitive j'aurais tendance a penser qu'une requete usine à gaz avec des sous requetes pour pouvoir faire tout ca sans avoir besoin de colonnes spécifiques au tri risque au final d'etre plus couteux que ces colonnes listing, mais n'ayant pas d'expérience pratique, je demande à ceux qui en ont :)

Reply

Marsh Posté le 24-12-2004 à 13:17:27    

J'abandonne :D
 
Je pense que je t'ai donné toutes les billes pour avancer (même si tu ne l'as apparemment pas vu). Si je me suis trompé, c'est peut-être parce que je n'ai pas assez d'infos sur ton problème ...

Reply

Marsh Posté le 24-12-2004 à 14:20:29    

bah tes solutions necessitent chaque fois un select sur toute la table, et ne sont pas applicables avec tri ou limite, ou alors se rapportent ah la saisie, qui elle ne pose aucun probleme, seul le listing par page et par ordre alphabétique avec une requete ne repportant que le nombre voulu de lignes pose probleme.
 
Merci d'avoir essayé de m'aider, le probleme (quand on ne le visualise pas parfaitement dans sa tete, ce que je suis le seul a pouvoir  faire en toute logique) n'est pas necessairement facile à comprendre :)

Reply

Marsh Posté le 24-12-2004 à 15:18:28    

J'ai uniquement apporté des idées concernant le remplissage des infos de listings (qui peuvent être comme tu le disais des champs supplémentaires dans la table jeu_t), le reste étant évident : une fois ces infos remplies, il suffit de faire :
 

Code :
  1. SELECT *
  2. FROM jeu_t
  3. ORDER BY listing_fr
  4. LIMIT '$debut', '$fin';

Reply

Marsh Posté le 24-12-2004 à 15:39:49    

ouioui mais sur ce point on est d'accord, soit 1 colonne pour chaque listing (cad pour chaque langue) dans la table t_jeu, soit une table t_listing comportant ces champs et l'id de la ligne de t_jeu correspondante. Je cherche juste à savoir si il n'y a pas moyen de se passer des ces colonnes de listing qui alourdissent pas mal la base

Reply

Sujets relatifs:

Leave a Replay

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