IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

SQL Procédural MySQL Discussion :

procstock, cursor et more than one row error


Sujet :

SQL Procédural MySQL

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Par défaut procstock, cursor et more than one row error
    Bonjour à tous,

    j'ai commencé avant hier à regarder les procédures stockées qui ont l'air très pratiques pour des requêtes redondantes comme j'ai besoin d'en faire.

    Pour l'instant je souhaite juste retourner la liste des objets contenus dans l'inventaire d'un joueur. Après il faudrait que la requête me renvoie la liste et la quantité pour chaque objet sachant que chaque objet est unique. Une "pile" de 8 tarte à la crème correspond donc à 8 entrées dans une table énorme.

    La structure:
    TABLE:
    -primary
    -colonne


    PLAYER: // Liste des joueurs
    -kId
    -name

    ENTITY_INVENTORY: // Liste les inventaires présents dans le monde (joueurs, coffres, marchand, etc...)
    -kId
    -playerkId

    INVENTORY_ITEM: // Liste des objets présents dans les inventaires
    -kId
    -itemTypekId
    -inventorykId

    ITEM_TYPE: // liste des types d'objets avec leurs noms
    -kId
    -name



    Bon alors récupérer tout ça dans une requête PHP avec PDO c'est du gâteau, mais avec une procstock je n'y arrive pas alors que j'y suis depuis 2 jours.

    Mon problème est que lorsqu'il n'y a qu'un seul enregistrement dans la table ITEM_LIST ca marche. Quand il y en a deux je reçois une erreur 1172: return more than one row (logique). J'ajoute donc un curseur dans la proc et là catastrophe, le serveur ne réponds pas...

    Je comprends franchement rien à ce qu'il se passe. Voilà ma procstock qui me renvoie bien une valeur si il n'y a qu'un objet dans l'inventaire ou subquery return more than... si il y a plusieurs objets. SQL me parle probablement de subquery car avant de récupérer les itemName je récupère leurs id dans la table inventory_item.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    BEGIN
     
    SELECT it.name
    INTO name
    FROM item_type it
    JOIN inventory_item ii 
    JOIN entity_inventory ei
    JOIN player p
    ON  it.kId = ii.itemTypekId AND ii.inventorykId = ei.kId AND ei.playerkId = p.kId WHERE p.mId = mId;
     
    END

    Maintenant j'ajoute le curseur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    BEGIN
    declare done int DEFAULT 0;
    DECLARE cursor1 CURSOR FOR
    SELECT it.name
    INTO name
    FROM item_type it
    JOIN inventory_item ii 
    JOIN entity_inventory ei
    JOIN player p
    ON  it.kId = ii.itemTypekId AND ii.inventorykId = ei.kId AND ei.playerkId = p.kId WHERE p.mId = mId;
     
     
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
    OPEN cursor1;
     
    REPEAT
    FETCH cursor1 INTO name;
    IF done = 0 THEN
    SELECT name;
    END IF;
    UNTIL done
    END REPEAT;
    CLOSE cursor1;
     
    END

    J'ai deux paramètres:
    IN: mId (VARCHAR) à la fin du WHERE de la déclaration du curseur
    OUT: name(VARCHAR) dans le REPEAT


    En espérant avoir fourni toutes les infos nécessaires et dans l'attente de vos réponses , bonne journée.
    Prisonier

  2. #2
    Membre Expert
    Avatar de ericd69
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2011
    Messages
    1 919
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2011
    Messages : 1 919
    Billets dans le blog
    1
    Par défaut
    salut,

    évite de faire un "on" final, la syntaxe normalisée c'est un "on" par "join" (tu mets la condition de jointure ainsi juste après la déclaration avec "join" à laquelle elle correspond)... idéalement toujours dire le type de jointure (inner/natural/cross/left)... ça évite les mauvaise surprises

    c'est normal, into sert à affecter une valeur dans une variable or les variables sont des types scalaires (tu ne peux pas y loger plusieurs valeurs comme c'est le cas d'une table)... d'où l'erreur que tu obtiens...

    en fait si tu veux lister les items tu n'a pas besoin de ça...

    juste de mettre la requête dans ta procédure avec 1 seul paramètre l'identifiant du joueur (évite de nommer pareil une variable et une colonne)
    et tu traite ta liste derrière coté applicatif...

    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    BEGIN
     
    SELECT it.name
    FROM item_type it
    JOIN inventory_item ii 
    JOIN entity_inventory ei
    JOIN player p
    ON  it.kId = ii.itemTypekId AND ii.inventorykId = ei.kId AND ei.playerkId = p.kId WHERE p.mId = mId;
     
    END

    si tu veux le faire dans une autre procédure 2 solutions:
    • une table temporaire (engine=memory, la plus efficace des solutions)
    • une chaine de caractères correspondant à la sérialisation des résultats (par exemple concaténation des résultats avec un séparateur)


    faut que tu sois clair dans l'organisation de tes traitements...

    les procédures stockées vont apporter une meilleure sécurisation et isolation entre application et sgbd et un gain de bande passante...

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Par défaut
    Merci pour ta réponse !
    Bon je comprends une partie de ce que tu dis:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    BEGIN
     
    SELECT it.name
    INTO name
    FROM item_type it
    JOIN inventory_item ii 
    ON it.kId = ii.itemTypekId 
    JOIN entity_inventory ei
    ON ii.inventorykId = ei.kId
    JOIN player p
    ON ei.playerkId = p.kId WHERE p.mId = player_mId;
     
    END
    Le problème c'est que je vais avoir une subquery returns more than one row encore vu que j'aurais plusieurs inventory_item.itemTypekId de renvoyés. Du coup il faut que je stock cette liste dans une table temporaire en memory engine c'est ça ?

    Puis ensuite je dois lire cette table temporaire mais je ne comprends pas comment. Le SELECT * va me renvoyer la meme erreur non ?
    Ou alors faire un CONCAT dans la requete ? Mais n'y a t-il pas une autre solution que concat ? Par exemple un VARCHAR ne suffira peut etre pas à renvoyer toutes les données et j'ai peur de partir sur des echanges trop lourds avec le serveur. Je précise que c'est pour un jeu vidéo et que cette fonction sert a mettre à jour l'inventaire du joueur dès qu'il récupère un nouvel objet, et c'est souvent.

    PS: J'ai lu que le cursor était largement déprécié par rapport à la temp_table, qu'en penses tu ?

  4. #4
    Membre Expert
    Avatar de ericd69
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2011
    Messages
    1 919
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2011
    Messages : 1 919
    Billets dans le blog
    1
    Par défaut
    alors je pense que tu as un gros problème à élucider: qu'est ce que tu fais de cette liste... qu'est ce qui la traite derrière...

    une variable avec out comme sens ne sert qu'à stocker une valeur UNIQUE !!!

    ensuite le curseur sert à faire un traitement par ligne, la table temporaire sert à stocker temporairement des donner pour des traitements via un autre select ensuite par exemple... c'est pas la même utilisation...

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Par défaut
    Alors je vais t'expliquer clairement ou les données vont, le comment c'est une autre histoire !
    Depuis la BDD organisé comme tu la voit, je vais essayer de trouver le bon cheminement:

    creation d'une table temporaire avec pour champs
    item_id|item_type_id

    Ensuite je dois calculer chaque quantité d' item_type_id (COUNT ?)et concaténer (CONCAT ?) le tout pour arriver à un String tel que:

    item_id item quantity|item_id item quantity|etc...

    Ce String est traité en aval en Java pour afficher l'inventaire au joueur.

    -------------------------------------------
    Infos diverses:
    Pour ce qui est du Java, je suis au point. J'arrive à me connecter à la BDD, exécuter une storedproc et récupérer le résultat pour le traiter.

    Pour cette requete je n'ai pas besoin de grand chose de plus mais après je devrais en faire une plus compliquée avec des transactions, j'ai donc vraiment besoin de comprendre le fonctionnement des storedproc pour mener à bien mon projet.

    Merci à toi.

  6. #6
    Membre Expert
    Avatar de ericd69
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2011
    Messages
    1 919
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2011
    Messages : 1 919
    Billets dans le blog
    1
    Par défaut
    y a plus simple et efficace... je pense...

    ton connecteur mysql java te permet de faire une boucle de lecture...

    d'où l'intérêt d'un simple select dans la procédure stockée... et directement faire un tableau...

    au lieu de faire différents reader (dont un parseur pour te cracher un tableau)

    faut être le plus simple et efficace pour un jeu surtout s'il est multijoueur et sur le réseau... car tu auras forcément la gestion de lags...

    de toute façon le client ne doit contenir aucun sql vu qu'il peut facilement être désassemblé et seul le serveur doit gérer les échanges avec le sgbd...

    après tu auras un vrai gain de temps d'exécution avec les procédures si tu utilises le principe des jobs (threads permanent ayant une connexion permanent au sgbd à qui ont passe des taches à traiter) car les procédures sont recompilées à chaque premier appel de chaque connexion)

    vois tu l'idée?

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Par défaut
    Je comprends a peu près le principe. Mais en fait le jeu est déjà sur une architecture client/server toutes deux en Java.

    J'ai sur le server:
    L'API Java du jeu
    APACHE utilisé avec PHP
    MySQL

    Côté client:
    Un web browser
    Le client du jeu

    Explications:
    Je ne me connecte jamais au client directement, je parle uniquement avec le server. Le jeu n'étant pas de ma conception (Minecraft*), toute la sécurité du client Java ne me concerne pas. Mon seul client Java est le server Java lui même.

    Java et PHP communiquent avec la BDD en flux double ainsi qu'entre eux, flux double aussi. Toutes ces histoires de flux sont gérées, c'est à dire que j'ai configurer les connexions et je peux transférer des données.

    Voilà pour l'architecture globale du projet.

    /*----------- Retour au SQL ----------*/
    Ca m'énerve parce que au fond je sais que SQL c'est très basique comme langage...

    Est ce que ton conseil c'est d'avoir une storedproc qui me concocte une table temporaire, puis de faire une requete préparée en Java pour lire son contenu comme je ferais avec PDO en PHP ?

    Merci d'entrer le paramètre de la gestion des lags et de la robustesse de la machine, c'est primordial pour mes choix. En fait la concaténation en String ça me faisait peur, j'y avais pensé il y a bien longtemps pour stocker l'inventaire des joueurs et j'avais abandonné le projet après beaucoup de mauvais avis.

    En fait pour la fin de ton message je suis pas sûr comprendre. Le thread est côté client de la BDD ? C'est pour limiter le nombre de compilation des requetes ?


    *

  8. #8
    Membre Expert
    Avatar de ericd69
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2011
    Messages
    1 919
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2011
    Messages : 1 919
    Billets dans le blog
    1
    Par défaut
    je vois tu fais un fork de minecraft...

    pourquoi ne pas faire que du pur java?

    php va être un goulot d'étranglement vu que c'est un langage interprété et que chaque appel ou réponse passe par des headers http via apache...

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Par défaut
    Un fork ? c'est quoi ?

    Parce qu'en PHP j'ai un niveau me permettant de faire ça tout seul en quelques semaines (je bosse chez moi). Java j'ai commencé il y a une semaine, je me vois mal coder un server de site web avec...

    EDIT:
    On est bien d'accord sur le fait que PHP et Java communiquent peu et ne se transmettent que des instructions, pas de données lourdes. Les données sont stockées sur SQL donc et Java et PHP y accèdent chacun de leur côté pour leurs besoins respectifs.

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Par défaut
    J'ai fait ça, maintenant je vais tenter un accès à la temp_table avec PDO:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    CREATE DEFINER=`root`@`localhost` PROCEDURE `getmIdInventory`(IN `playermId` VARCHAR(40))
    BEGIN
    DROP TEMPORARY TABLE IF EXISTS mIdInventory;
    CREATE TEMPORARY TABLE mIdInventory
    AS
    SELECT it.mId
    FROM item_type it
    INNER JOIN inventory_item ii 
    ON it.kId = ii.itemTypekId 
    INNER JOIN entity_inventory ei
    ON ii.inventorykId = ei.kId
    INNER JOIN player p
    ON ei.playerkId = p.kId WHERE p.mId = playermId;
    END

  11. #11
    Membre Expert
    Avatar de ericd69
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2011
    Messages
    1 919
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2011
    Messages : 1 919
    Billets dans le blog
    1
    Par défaut
    fork=projet dérivé dans le langage info car fork c'est la copie de processus dans certain langage et environnements...

    php n'est pas pensé du tout pour faire du temps réel et n'est pas multithread... je vois pas bien à quoi il te sert par rapport à l'api java du jeu...

    l'api doit te permettre d'avoir déjà ton serveur quasi tout prêt...

    après comme je te l'ai dit c'est une question de traitement...

    est ce qu'il est plus rentable de:
    • générer tes résultats
    • les concaténer
    • les déconcaténer
    • les mettre dans un tableau
    ou:
    • générer tes résultats
    • les mettre dans un tableau

    et de comment tu peux faire ton traitement (ce que tu peux toucher ou faire dans ton code)

    ta procédure devrais être celle que je te montrais:
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    create procedure lis_inventaire(in player_mId int unsigned)
    BEGIN
     
    SELECT it.name as item,count(it.name) as nombre
    FROM item_type it
    JOIN inventory_item ii 
    ON it.kId = ii.itemTypekId 
    JOIN entity_inventory ei
    ON ii.inventorykId = ei.kId
    JOIN player p
    ON ei.playerkId = p.kId WHERE p.mId = player_mId
    group by item;
    END
    ça te liste les items et leur nombre

    pas besoin de transformer ça pour récupérer directement en php ou java avec l'api mysql qui va bien et directement former un tableau pour en faire ce que tu veux derrière...

    faut être efficace...faire simple...

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Par défaut
    Tu n'a pas compris. Il y a un site web et un serveur minecraft tout deux reliés sur une meme base de données. Par exemple si je souhaite réaliser une vente entre deux joueurs à partir de minecraft, je dois passer par le siteweb.
    Le site web n'est pas plus en temps réel que n’importe quel jeu par navigateur.

    Java accède à la BDD pour modifier le jeu. PHP accède à la BDD pour modifier le siteweb.

    Il faut comprendre que c'est deux projets qui communiquent ensemble via une base de données commune et par envoi de commande à l'un et à l'autre.

    Bien d'accord avec faire simple pour la requete mais je savais pas comment faire

    Dans ta requete il ne vaut pas mieux faire INNER JOIN comme tu me le disait plus haut ?

    Je vais faire comme tu dis et traiter une temp table en Java directement (ou PHP pour le site web).

    Par contre il y a encore des termes que je ne comprends pas comme UNSIGNED, je vais me renseigner. Les deux AS à la suite aussi c'est flou...
    De plus, où est la table temporaire dans ton exemple ?

  13. #13
    Membre Expert
    Avatar de ericd69
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2011
    Messages
    1 919
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2011
    Messages : 1 919
    Billets dans le blog
    1
    Par défaut
    oui c'est mieux inner join là

    en fait si tu as un ou plusieurs select faudra juste faire des boucles de lecture comme si tu lisais des requêtes select simple... ça change rien...

    faut dire que tu n'étais pas très clair dans tes explications au départ...

    donc maintenant tu as une base pour récupérer tes actions...

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Par défaut
    En fait je comprends que la procédure stockée peut retourner plusieurs lignes mais pas dans une variable OUT. Merci beaucoup pour ton aide mais ne crois pas te débarasser de moi comme ça, j'en ai une autre plus balèze avec des tables temporaire et des transactions !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 18/09/2013, 09h14
  2. Problème: "Query returns more than one row"
    Par ctobini dans le forum Requêtes
    Réponses: 1
    Dernier message: 27/09/2007, 11h48
  3. Réponses: 4
    Dernier message: 25/01/2007, 15h02
  4. Réponses: 3
    Dernier message: 08/12/2006, 17h28
  5. ORA-01427: single-row subquery returns more than one row
    Par hadid dans le forum Langage SQL
    Réponses: 3
    Dernier message: 31/10/2006, 15h35

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo