2016-10-18 3 views
1

Essayer de faire une requête assez simple dans Propel 2. J'ai une table Person et une table Possession - les personnes peuvent avoir beaucoup de possessions mais seulement un de chaque type de possession. Donc, une personne peut avoir 1 livre, 1 voiture, etc. J'essaie d'écrire une requête dans Propel qui rendra toutes les personnes avec leur nom de voiture à condition qu'ils aient une voiture. Voici le code et la requête résultante:Propel ajoute CROSS JOIN pour interroger lors de l'utilisation d'un alias pour JOIN tables

$x = PersonQuery::create() 
    ->groupById() 
    ->leftJoinPossession() 
    ->addJoinCondition('Possession','Possession.possession_type = ?', 'car') 
    ->withColumn('Possession.possession_name', 'CarName') 
    ->where('Possession.possession_name IS NOT NULL') 
    ->find() 
    ->toString(); 

//resulting sql 
SELECT person.id, possession.possession_name as CarName 
FROM person 
LEFT JOIN possession ON (person.id=possession.person_id AND possession.possession_type = 'car') 
WHERE possession.possession_name IS NOT NULL; 

Cela fonctionne comme prévu. Cependant, j'ai besoin de faire plusieurs jointures à la table de possession (obtenir les livres pour chaque personne par exemple) donc j'ai besoin d'utiliser un alias. Voici ce qui se passe quand je modifie la requête précédente juste pour utiliser un alias pour la table de possession (et pour obtenir aussi le livre pour chaque personne):

$x = PersonQuery::create() 
    ->groupById() 
    ->leftJoinPossession('p') 
    ->addJoinCondition('p','p.possession_type = ?', 'car') 
    ->leftJoinPossession('p2') 
    ->addJoinCondition('p2','p2.possession_type = ?', 'book') 
    ->withColumn('p.possession_name', 'CarName') 
    ->withColumn('p2.possession_name', 'BookName') 
    ->where('p.possession_name IS NOT NULL') 
    ->find() 
    ->toString(); 

//resulting sql 
SELECT person.id, p.possession_name as CarName, p2.possession_name as BookName 
FROM person 
CROSS JOIN possession 
LEFT JOIN possession p ON (person.id=p.person_id AND p.possession_type = 'car') 
LEFT JOIN possession p2 ON (person.id=p2.person_id AND p2.possession_type = 'book') 
WHERE p.possession_name IS NOT NULL; 

Comme vous pouvez le voir, pour une raison quelconque Propel ajoute: « CROSS JOIN possession "à la requête. Cela ne change pas le résultat de la requête mais la rend extrêmement lente. Des idées sur comment je peux dire Propel de ne pas utiliser un CROSS JOIN tout en utilisant un alias pour ma table jointe? (Le CROSS JOIN disparaît aussi si je retire la clause 'where')

Répondre

1

avons trouvé une solution à ce problème. Le problème était que lorsque j'essayais de faire plusieurs jointures à gauche sur la même table (en utilisant des alias), Propel incluait également une jointure croisée à cette table. Cependant, j'ai trouvé que si la première jointure n'utilisait pas d'alias, Propel n'ajouterait pas la jointure croisée.

Donc, pour résumer, cela ne fonctionnait pas:

$x = PersonQuery::create() 
    ->groupById() 
    ->leftJoinPossession('p') 
    ->addJoinCondition('p','p.possession_type = ?', 'car') 
    ->leftJoinPossession('p2') 
    ->addJoinCondition('p2','p2.possession_type = ?', 'book') 
    ->withColumn('p.possession_name', 'CarName') 
    ->withColumn('p2.possession_name', 'BookName') 
    ->where('p.possession_name IS NOT NULL') 
    ->find() 
    ->toString(); 

//resulting sql 
SELECT person.id, p.possession_name as CarName, p2.possession_name as BookName 
FROM person 
CROSS JOIN possession 
LEFT JOIN possession p ON (person.id=p.person_id AND p.possession_type = 'car') 
LEFT JOIN possession p2 ON (person.id=p2.person_id AND p2.possession_type = 'book') 
WHERE p.possession_name IS NOT NULL; 

Mais cela a fonctionné comme prévu:

$x = PersonQuery::create() 
    ->groupById() 
    ->leftJoinPossession() //changed 
    ->addJoinCondition('Possession','Possession.possession_type = ?', 'car') //changed 
    ->leftJoinPossession('p2') 
    ->addJoinCondition('p2','p2.possession_type = ?', 'book') 
    ->withColumn('p.possession_name', 'CarName') 
    ->withColumn('p2.possession_name', 'BookName') 
    ->where('p.possession_name IS NOT NULL') 
    ->find() 
    ->toString(); 

//resulting sql 
SELECT person.id, p.possession_name as CarName, p2.possession_name as BookName 
FROM person 
LEFT JOIN possession ON (person.id=posession.person_id AND posession.possession_type = 'car') 
LEFT JOIN possession p2 ON (person.id=p2.person_id AND p2.possession_type = 'book') 
WHERE p.possession_name IS NOT NULL; 
1

Pourquoi êtes-vous partie pour rejoindre? Si je comprends bien votre message, vous voulez seulement des résultats pour les personnes qui ont effectivement une voiture. Donc, ce qui suit devrait fonctionner:

PersonQuery::create() 
    ->withColumn('Possession.possession_name', 'CarName') 
    ->usePossessionQuery() 
    ->filterByPossessionType('car') 
    ->endUse() 
    ->find() 
    ->toString(); 

useXxxQuery() peut également être donné un alias, vous devriez donc être en mesure d'accomplir la même chose, mais plus facilement :)

EDIT: un exemple avec plusieurs jointures, selon votre deuxième exemple:

PersonQuery::create() 
    ->withColumn('possession_car.possession_name', 'CarName') 
    ->withColumn('possession_book.possession_name', 'BookName') 
    ->usePossessionQuery('possession_car') 
    ->filterByPossessionType('car') 
    ->endUse() 
    ->usePossessionQuery('possession_book') 
    ->filterByPossessionType('book') 
    ->endUse() 
    ->find() 
    ->toString(); 
+0

Je dois pouvoir utiliser plusieurs jointures à la table de possession. J'ai édité le deuxième bloc de code dans mon post original pour montrer ce que je dois faire un peu plus clair - je ne pense pas que la construction de la requête à votre façon fonctionnera pour ce que j'essaie de faire. – zeke

+1

Je ne vois pas pourquoi ce ne serait pas le cas. Comme je l'ai dit, vous pouvez spécifier un alias pour usePossessionQuery (c'est le premier paramètre). Je vais mettre à jour mon message avec un exemple de plusieurs jointures. – chocochaos

0

Cela ressemble vraiment à un bug. L'avez-vous signalé? En règle générale, Propel va vérifier quelle relation/alias vos critères d'où pointent, puis assurez-vous que toutes ces tables sont incluses dans la requête. S'il n'existe aucune clause de jointure pour cette relation/alias, elle l'inclura avec une CROSS JOIN.

La méthode ->where() n'a aucun alias de relation fourni via un paramètre de méthode (comme filterByX a), et alors que propel voit que le 'p.' une partie de la condition where() est mappée à la table de possession, elle ne comprend pas le fait qu'elle est parvenue à cette conclusion en utilisant l'alias relation (c'est le bogue), alors propel pense que where() veut vérifier par rapport à la table possession (sans alias), et donc la jointure croisée est incluse.

Votre solution de contournement ne fonctionne donc à cause de ce bug, et il serait plus clair d'écrire:

->where('possession.possession_name IS NOT NULL') 

...pour clarifier que là où() se réfère à la table de possession non-biaisée et non pas une relation spécifique alias.

Si je ne me trompe pas, votre solution de contournement ne doit pas fonctionner si vous avez fait référence le deuxième alias:

->where('p2.possession_name IS NOT NULL') 

(il serait encore référence à la première relation)

PS IMHO, Propel devrait lancer une exception à la place de cette «jointure» croisée automagique de la requête, puisque la jointure croisée est le plus souvent une conséquence indésirable d'une faute de frappe ou d'une utilisation incorrecte des méthodes de requête.

Pendant ce temps, voici un point essentiel avec le code d'aide qui vous permet de vérifier ces conditions et lancer des exceptions si nécessaire: https://gist.github.com/motin/2b00295ca71bb876f9873712544dd077