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

ORM PHP Discussion :

[Doctrine] fait une requête SQL de trop avec deux innerJoin()


Sujet :

ORM PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Août 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 12
    Par défaut [Doctrine] fait une requête SQL de trop avec deux innerJoin()
    Bonsoir,

    Dans mon projet actuel j'utilise Symfony avec Doctrine. Ceci est une première expérience avec ce framework. Jusqu'alors je travaillais avec Zend. Tout se passe bien jusqu'ici. Enfin, presque.

    La situation qui me fait mal à la tête est la suivante. J'ai trois tables:
    - messages (avec deux clés étrangères: users_id_us - id du récepteur et messages_content_id_mc - id du contenu de message)
    - messages_content (avec une clé étrangère : users_id_us - id de l'auteur du message)
    - users - (sans clés étrangères, la clé primaire : id_us).

    Maintenant je cherche à récupérer tous les messages pour un utilisateur connecté. La mission partiellement réussie. Je m'explique.

    La requête Doctrine que j'utilise pour cette tâche est la suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $q = Doctrine_Query::create()
    	->select('id_me, users_id_us, state_me, type_me, mc.title_mc, us.login_us') 
    	->from('messages m')
    	->innerJoin('m.messages_content mc')
    	->innerJoin('mc.Users us')	
    	->where('users_id_us = ?', $user)
    	->limit($opt['limit'])
    	->offset($opt['offset'])
    	->orderBy($opt['order']);
    Tout se passe bien: j'ai tous les éléments qu'il faut, sauf... Sauf que Doctrine crée deux requêtes SQL au lieu d'une:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT DISTINCT m3.id_me FROM messages m3 
    INNER JOIN messages_content m4 ON m3.messages_content_id_mc = m4.id_mc 
    INNER JOIN users u2 ON m4.users_id_us = u2.id_us 
    WHERE m3.users_id_us = '6' 
    ORDER BY m3.id_me DESC LIMIT 2
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT m.id_me AS m__id_me, m.users_id_us AS m__users_id_us, m.state_me AS m__state_me, m.type_me AS m__type_me, m2.id_mc AS m2__id_mc, m2.title_mc AS m2__title_mc, u.id_us AS u__id_us, u.login_us AS u__login_us 
    FROM messages m 
    INNER JOIN messages_content m2 ON m.messages_content_id_mc = m2.id_mc
     INNER JOIN users u ON m2.users_id_us = u.id_us 
    WHERE m.id_me IN ('11') AND (m.users_id_us = '6') 
    ORDER BY m.id_me DESC
    Et pourtant, on peut effectuer la même action avec une seule requête SQL:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT m.id_me, m.users_id_us, m.state_me, m.type_me, mc.title_mc, u.login_us 
    FROM messages m 
    JOIN messages_content mc ON mc.id_mc = m.messages_content_id_mc 
    JOIN users u ON u.id_us = mc.users_id_us 
    WHERE m.users_id_us = 6;
    Avez-vous une idée quelconque pourquoi Doctrine crée deux requêtes tandis que logiquement on devrait en utiliser une ? Je cherche à le comprendre pour pouvoir modifier ma requête DQL. Cependant, je n'ai plus d'idées.

    Merci d'avance pour votre participation.

  2. #2
    Membre chevronné
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    396
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 396
    Par défaut
    Bonjour,

    je n'ai pas regardé tout le contenu des requêtes mais, déjà, pour bien comprendre ton problème :
    - ta table Message est une table d'association entre User et MessageContent ?
    - si oui, ce que tu cherches à faire est bien de récupérer tous les MessageContent d'un User donné ?

    Si oui, c'est que tu cherches à faire une relation n-n entre User et MessageContent et la solution viendrait plutôt de ton schéma : voir les relations Many-to-Many.

    Tu disposeras alors, sur un objet User, d'une fonction getMessageContents() qui te retournera tous les MessageContent d'un User, sans avoir toi-même à écrire de requête.

    Si j'ai mal compris le problème, merci de le dire

  3. #3
    Membre habitué
    Profil pro
    Inscrit en
    Août 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 12
    Par défaut
    Citation Envoyé par bilbonec Voir le message
    Bonjour,

    je n'ai pas regardé tout le contenu des requêtes mais, déjà, pour bien comprendre ton problème :
    - ta table Message est une table d'association entre User et MessageContent ?
    - si oui, ce que tu cherches à faire est bien de récupérer tous les MessageContent d'un User donné ?

    Si oui, c'est que tu cherches à faire une relation n-n entre User et MessageContent et la solution viendrait plutôt de ton schéma : voir les relations Many-to-Many.

    Tu disposeras alors, sur un objet User, d'une fonction getMessageContents() qui te retournera tous les MessageContent d'un User, sans avoir toi-même à écrire de requête.

    Si j'ai mal compris le problème, merci de le dire
    OK, je serai plus explicite. Je cherche à faire les relations entre:
    - Users et Messages (un utilisateur peut recevoir un ou plusieurs messages; clé étrangère du côté Messages) --> ici l'utilisateur est identifié en tant que le récepteur
    - Messages et Messages_content (un contenu de message peut appartenir à un ou plusieurs messages)
    - Messages_content et Users (un contenu peut être écrit seulement par un utilisateur, mais un utilisateur peut en écrire plusieurs) ---> ici l'utilisateur est identifié en tant qu'expéditeur

    Pratiquement c'est l'envoi d'un message à plusieurs utilisateurs. J'aurais pu faire cela plus simplement avec deux tables: Messages (qui contenait les données de la table Messages_content) et Users. Mais je préfère ne pas prendre le risque de faire exploser la table Messages.

    Est-ce que c'est maintenant plus clair ?
    Merci.

  4. #4
    Membre chevronné
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    396
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 396
    Par défaut
    Ok pour les explicitations, mais maintenant c'est moi qui ait mal à la tête >_<

    Tu pourrais poster ton schema.yml pour ces 3 objets Doctrine ? Ca permettra de mieux visualiser ton schema.

    Juste une question. Si tu fais un :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    echo $q->getSqlQuery();
    juste après ta requête DQL, ça te donne la même chose ?

    EDIT
    A lire la doc sur un sujet plus ou moins proche : limit clause in Doctrine, je me dis que la raison est peut-être une configuration de Doctrine_Core.

    Ceci dit, j'aurais du mal à t'aider beaucoup sur ce sujet.

  5. #5
    Membre émérite Avatar de Herode
    Homme Profil pro
    Développeur Web
    Inscrit en
    Mars 2005
    Messages
    825
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Savoie (Rhône Alpes)

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

    Informations forums :
    Inscription : Mars 2005
    Messages : 825
    Par défaut
    Quand tu fais un Doctrine_Query::create(); Doctrine ne crée, pour autant que je sache, qu'une seule requête. La première requête correspond bien à ce que tu cherches, la seconde est probablement créée plus tard, par exemple lorsque Doctrine cherche à instancier un objet User pour l'expéditeur du message.

    NB : Tu devrais pouvoir vérifier (ou infirmer) cela en plaçant une trace de log juste après le Doctrine_Query::create() pour afficher le contenu de la requête créée par cet appel.

  6. #6
    Expert confirmé
    Avatar de Michel Rotta
    Homme Profil pro
    DPO
    Inscrit en
    Septembre 2005
    Messages
    4 954
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : DPO
    Secteur : Distribution

    Informations forums :
    Inscription : Septembre 2005
    Messages : 4 954
    Par défaut
    Le create() est sensé ne construire qu'une requête, si le schéma colle et qu'il n'est pas trop violé !


    Même remarque que Bilbonec, pas de solution sans un œil attentif sur le shema.yml

  7. #7
    Membre habitué
    Profil pro
    Inscrit en
    Août 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 12
    Par défaut
    Salut,

    Merci à tous pour vos conseils. La solution est venue toute seule après une semaine de rien faire là-dessous.

    J'explique ma solution qui n'est pas une baguette magique. J'ai tout simplement modifié la relation dans ma requête DQL.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $q = Doctrine_Query::create()
    	->select('id_me, users_id_us, state_me, type_me, mc.title_mc, us.login_us') 
    	->from('messages m')
    	->innerJoin('m.messages_content mc')
    	->innerJoin('mc.Users us')	
    	->where('users_id_us = ?', $user)
    	->limit($opt['limit'])
    	->offset($opt['offset'])
    	->orderBy($opt['order']);
    est devenu:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $q = Doctrine_Query::create()
    	->select('id_me, users_id_us, state_me, type_me, mc.title_mc, us.login_us') 
    	->from('messages m')
    	->innerJoin('m.messages_content mc')
    	->innerJoin('m.Users us ON mc.users_id_us=us.id_us')	
    	->where('users_id_us = ?', $user)
    	->limit($opt['limit'])
    	->offset($opt['offset'])
    	->orderBy($opt['order']);
    Et du côte SQL, je reçois une seule requête:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #
     
    SELECT m.id_me AS m__id_me, m.users_id_us AS m__users_id_us, m.state_me AS m__state_me, m.type_me AS m__type_me, m2.id_mc AS m2__id_mc, m2.title_mc AS m2__title_mc, u.id_us AS u__id_us, u.login_us AS u__login_us FROM messages m INNER JOIN messages_content m2 ON m.messages_content_id_mc = m2.id_mc INNER JOIN users u ON (m2.users_id_us = u.id_us) WHERE (m.users_id_us = '7') ORDER BY m.id_me DESC LIMIT 2

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

Discussions similaires

  1. [MySQL] Problème avec une requête SQL
    Par chobol dans le forum PHP & Base de données
    Réponses: 12
    Dernier message: 11/05/2006, 12h29
  2. [VB]Problème avec une requête SQL
    Par Tyrael62 dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 18/03/2006, 17h47
  3. [MySQL] concaténer une requête sql avec condition
    Par maliak dans le forum PHP & Base de données
    Réponses: 6
    Dernier message: 10/02/2006, 12h01
  4. Une requête SQl avec concaténation
    Par Bobybx dans le forum Langage SQL
    Réponses: 2
    Dernier message: 19/01/2006, 10h18
  5. [MySQL] probleme d'une requête SQL crée avec phpmyadmin
    Par ghita269 dans le forum PHP & Base de données
    Réponses: 7
    Dernier message: 24/10/2005, 10h15

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