Décaler des valeurs de champs MYSQL *RESOLU*

Décaler des valeurs de champs MYSQL *RESOLU* - SQL/NoSQL - Programmation

Marsh Posté le 16-06-2009 à 15:27:22    

Bonjour,
 
J'ai une table d'utilisateurs comprenant un champ idUser et un champ nom_utilisateur.
 
J'ai une autre table pour gérer des affinités entre utilisateurs. A chaque utilisateur sont associés les utilisateurs "amis", hiérarchisés selon une note d'affinité.
Les champs de cette table ressemblent à ceci :
Index, idUser, idUser01, note01, iduser02, note02, ... idUser20, note20.
 
Il peut arriver qu'un utilisateur disparaisse.
 
Je le supprime donc de la table des utilisateurs.
Je le supprime aussi de la table des affinités (suppression de la ligne où idUser = le numéro de l'utilisateur supprimé).
 
Mais ce numéro peut aussi exister zéro ou une fois dans chacune des autres lignes, par exemple dans idUser05 pour l'utilisateur U et dans idUser12 pour l'utilisateur U+n, à chaque fois suivi par la note d'affinité avec l'utilisateur considéré (note05 pour U et note12 pour U+n).
 
Il faut donc supprimer pour l'un idUser05 et note05 pour l'un, et idUser12 et note12 pour l'autre. Enfin, il faut décaler les valeurs des champs qui viennent après :
 
Pour l'idUser n° U, idUser05 prendra la valeur de idUser06, qui prendra la valeur de id07, etc. et id20 prendra la valeur 0.
Le notes seront décalées elles aussi : note05 prendra la valeur de note06, qui prendra la valeur de note07 etc., note20 passant à 0.
Pour l'idUser n°U+n, même traitement récursif, avec un décalage à partir de l'idUser12 et de la note12.
 
Le problème consiste donc à repérer les lignes où l'id de l'utilisateur supprimé apparait et, dans ces lignes, à effectuer le décalage récursif des champs.
 
Et là, je coince un p'tit peu pour trouver des requêtes SQL à la fois efficaces et judicieuses...
 
Si quelqu'un a l'expérience de ce genre de problème, je suis preneur d'infos, merci !

Message cité 1 fois
Message édité par Kiosquec le 17-06-2009 à 18:04:23
Reply

Marsh Posté le 16-06-2009 à 15:27:22   

Reply

Marsh Posté le 16-06-2009 à 15:48:44    

J'ai tenté ceci, qui est une première étape :
 
UPDATE `affinites` SET `idUser01`=(SELECT `idUser02`) WHERE`idUser`=1;
UPDATE `affinites` SET `note01`=(SELECT `note02`) WHERE`idUser`=1;
 
Ce n'est pas tout à fait l'objectif, car ici je travaille sur la ligne à supprimer. Il faudrait écrire WHERE `idUser01`= 1 (le 1 étant à paramétrer pour travailler sur d'autres utilisateurs, mais du point de vue de la syntaxe, ça passe, et au moinsle test n'a pas flanqué la pagaille dans ma table, en ne changeant que 2 valeurs repérées d'avance.
Sauf qu'il faut répéter la requête 20 fois pour décaler mes 20 champs.
Il doit bien y avoir une solution plus directe , non ?

Reply

Marsh Posté le 16-06-2009 à 15:52:08    

Kiosquec a écrit :

J'ai une autre table pour gérer des affinités entre utilisateurs. A chaque utilisateur sont associés les utilisateurs "amis", hiérarchisés selon une note d'affinité.
Les champs de cette table ressemblent à ceci :
Index, idUser, idUser01, note01, iduser02, note02, ... idUser20, note20.


[:vomi]
 
Tu as entendu parler de normalisation ?
 
Si tu peux, ta table "AFFINITES" deviendrait :
AFFINITES (id_affinite, id_user,id_ami,note)
Avec relation 0,n sur USER pour les deux clés etrangères id_user et id_ami (un user peut avoir 0, un ou plusieurs amis, et un user peut être ami de 0, un ou plusieurs autres users).
=> tu n'as plus qu'à mettre la suppression en cascade (quand tu supprimes un user, tu supprimes les enregistrements de AFFINITES qui le concernent).
Et même si le SGBD ne supporte pas ça, ton boulot est quand-même sacrément simplifié...

Reply

Marsh Posté le 16-06-2009 à 15:55:38    

C'est si gênant que ça d'avoir des décalages ? Car ce que tu veux faire c'est galère pour pas grand chose.

 

Edit : +1 macgawel j'ai lu un peu vite j'ai pas vu que c'était aussi dégueulasse que ça sa table :D
C'est à se demander pourquoi utiliser une BDD pour faire un truc pareil. :p


Message édité par Deamon le 16-06-2009 à 15:58:00
Reply

Marsh Posté le 16-06-2009 à 16:15:55    

J'utilise une table existante et n'ai pas le droit d'en créer une autre ni de modifier ses champs. Elle m'est fournie en sortie d'un programme sur lequel je n'ai pas à intervenir. Mais je peux essayer de convaincre en plus haut lieu de faire mieux quand même si j'ai les bons arguments à proposer.
 
Et de toute façon je reste preneur de toute info me permettant de dépasser le stade du SELECT * FROM table, mes notions de MYSQL étant très largement perfectibles.
 
La normalisation a l'air d'être un principe intéressant. Reste à comprendre comment ça marche. Il y a une exemple avec des requêtes réelles quelque part ?
 
1er edit : Le non décalage n'est peut-être pas si grave, car la mise à jour automatique de la table se fait sur des intervalles relativement courts. Supprimer de mon côté la référence à l'idUser suffirait probablement à éviter la création de listes d'amis erronées, en attendant la mise à jour automatique suivante. Il vaut mieux provisoirement oublier un ami que de cliquer sur un lien qui envoie aux pelotes. Autrement dit, effectivement je me casse peut-être la tête pour pas grand-chose.
 
D'après ce que j'ai lu (survolé) sur la normalisation, je me demande si ce n'est pas un peu lourd à générer dans le cas qui m'intéresse. Actuellement, un programme Java crée périodiquement une table d'affinités rustique mais simple à utiliser... tant qu'un utilisateur ne se désinscrit pas dans l'intervalle. Il suffit en effet de parcourir un enregistrement pour en tirer une bête liste déroulante propre à l'utilisateur connecté. Il suffirait de ne pas tenir compte d'une info idUser intercalée si elle est (provisoirement) mise à zéro.
Finalement, la table serait sans doute crade si elle était permanente et devait être mise à jour en fonction des événements internes, mais la solution retenue n'est pas si mauvaise pour l'usage qui en est fait.
 
Je vais quand même me pencher sur cette histoire de normalisation.

Message cité 2 fois
Message édité par Kiosquec le 16-06-2009 à 16:56:10
Reply

Marsh Posté le 16-06-2009 à 16:57:22    

Kiosquec a écrit :

J'utilise une table existante et n'ai pas le droit d'en créer une autre ni de modifier ses champs. Elle m'est fournie en sortie d'un programme sur lequel je n'ai pas à intervenir. Mais je peux essayer de convaincre en plus haut lieu de faire mieux quand même si j'ai les bons arguments à proposer.

Pouvoir faire ton boulot sans pondre une usine à gaz, ça me semble défendable comme argument  :sarcastic:  
 

Kiosquec a écrit :

La normalisation a l'air d'être un principe intéressant.

Disons que c'est ce qui va faire la différence entre un SGBD-R et un fichier Excel...
 
Vouloir "faire" du MySQL sans avoir au moins des notions sur les SGBD-R, c'est un peu comme si tu voulais faire du PHP sans connaître le web  :lol:  
Si j'ai un conseil à te donner : va t'acheter un bouquin sur les BDD, demande une formation ou cherche de la doc sur le net, mais fais quelque chose !
 
Quant à te donner un exemple avec des requêtes... La normalisation intervenant à la conception, c'est un peu difficile !
Mais en reprennant ton bousin :
Comment fais-tu, avec tes tables, pour trouver les gens qui sont amis avec Machin ?
Avec une base relationnelle on aurait :
USERS(id, nom)
AFFINITES(id, user_id,ami_id,note)
Et la requête :

Code :
  1. SELECT USERS.nom FROM AFFINITES,USERS WHERE USERS.id = AFFINITES.ami_id;


 
Autre exemple : si tu dois rajouter un "ami" (pour en avoir 21) comment feras-tu ? Il va falloir modifier la table et ensuite le programme pour qu'il gère les 21 amis.
Alors qu'avec une base normalisée on s'en fiche de connaître le nombre maximum d'amis...
 
...
 
Dans ton cas, si tu ne peux pas modifier le schéma, je dirais bien de traiter via le programme. Tu crées un tableau (valable aussi avec un fichier texte)
TAffinites(id_user => LesAffinites) avec LesAffinites = "Iduser01;note01;...IdUser20;note20;"
Il te restes à virer IdUseràVirer;[0-9][0-9]; de LesAffinites, et reconstituer la table à partir du tableau...

Reply

Marsh Posté le 16-06-2009 à 17:18:33    

Kiosquec a écrit :

Finalement, la table serait sans doute crade si elle était permanente et devait être mise à jour en fonction des événements internes, mais la solution retenue n'est pas si mauvaise pour l'usage qui en est fait.


Jusqu'au jour où il faudra gérer 21 amis :D  
 
Et franchement, j'ai du mal à saisir l'intérêt de ce genre de truc : utiliser Java pour générer une table MySQL non relationnelle, autant utiliser un fichier plat (ou du XML si tu veux la jouer moderne) !  :pfff:

Reply

Marsh Posté le 16-06-2009 à 17:52:19    

Voilà pas mal de remarques de bon sens, et de bonnes raisons pour essayer d'aller plus loin. En commençant par les tableaux peut-être, comme suggéré par macgawel...
 
Précision : le programme java n'a pas pour seule fonction de générer une table qui sera utilisée par un serveur Web. Il sert à faire une foule de calculs complexes, dont la table en question n'est qu'une petite retombée parmi d'autres, histoire d'apporter un plus à un moteur de recommandations. Genre, nous avons recommandé des choses plus ou moins similaires à ces 0 à 20 personnes. 20, cela paraît très suffisant dans le cadre d'une expérimentation. Mais OK, ça ne dispense pas d'utiliser des méthodes plus modernes, pour passer à 21 au besoin. Même dans cette hypothèse, je n'interviendrai qu'en bout de chaîne, avec ce qu'on me fournira.

Reply

Marsh Posté le 17-06-2009 à 18:39:36    

Ne pouvant modifier la structure de la table, qui ne dépend pas de moi, j'ai opté pour le passage par un tableau.
Mon tableau comprend 3 champs :
idUser, idAmi, noteAmi.
 
Je récupère les données de la table et les traite ligne par ligne.
Si je trouve en tête de ligne l'idUser à supprimer, je passe directement à la ligne suivante sans traitement.
 
Pour les autres idUser, je décompose la ligne lue pour en extraire l'idAmi et la noteAmi de rang n, et les fais précéder du numéro de l'idUser en cours. Si l'idAmi est l'idUser à éliminer, je passe directement à l'idAmi et à la noteAmi suivants.
 
Si au rang r j'ai un idAmi et une noteAmi = 0, je passe à la ligne suivante.
 
Mon tableau est donc filtré. Il ne contient que des données utiles, avec toutefois des doublons car si A à pour ami B, j'aurai aussi B a pour ami A. Mais il me semble plus simple de les conserver pour ne pas compliquer la suite du traitement.
 
Si et seulement si j'ai détecté l'idUser à virer, j'exécute la mise à jour de la table.
 
Pour cela, je crée une table provisoire dans laquelle j'injecte les données du tableau, en complétant à 0 les champs non servis. Ainsi, je peux utiliser une requête standardisée qui fonctionne quel que soit le nombre de champs utiles de chaque ligne (entre 0 et 20). Mais il n'est peut-être pas plus compliqué de bâtir une requête à nombre de champs variable, en utilisant alors des valeurs par défaut à 0 dans la création de la table. Pas essayé.
 
Ensuite j'élimine la table originale, et je renomme la table provisoire pour remplacer la table effacée.
 
Ce n'est pas vraiment top, car il pourrait y avoir une transaction sur la table à effacer avant la fin du traitement, transaction qui sera perdue. Le cas est peu probable, mais la loi de l'emmerdement maximum sévit toujours de nos jours, alors...
Ce n'est en fait pas bien  grave car toutes les 3 minutes une nouvelle table à jour se remet en place. Ma manip a pour seule fonction de limiter les risques de créer un lien qui serait faux pendant une durée comprise entre 0 seconde et moins de 3 minutes. Mais bon, dans d'autres applis, ça pourrait être vraiment gênant.
 
Ma prochaine lacune à combler portera donc sur les conflits d'accès et le blocage des tables sous MySQL. Si quelqu'un a des tuyaux ou un tutorial à proposer...

Reply

Sujets relatifs:

Leave a Replay

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