尝试在Propel 2中进行一个相当简单的查询。我有一个Person表和一个Possession表 - 人可以有很多财产,但每个拥有类型只有一个。因此,一个人可以拥有1本书,1辆汽车等。我试图在Propel中写一个查询,如果他们有车,将返回所有人的汽车名称。这是代码和结果查询:
$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;
这可以按预期工作。但是,我需要对拥有表进行多次连接(例如为每个人获取书籍),所以我需要使用别名。这是当我修改上一个查询只是为了使用拥有表的别名(以及为每个人获取书籍)时会发生什么:
$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;
正如你所看到的,出于某种原因,Propel增加了" CROSS JOIN拥有"到查询。这不会改变查询的结果,但会使它非常慢。有关如何告诉Propel不使用CROSS JOIN同时仍然使用联接表的别名的任何想法? (如果我拿出' where'子句)
,CROSS JOIN也会消失答案 0 :(得分:2)
你为什么要离开?如果我正确理解你的帖子你只想要实际拥有汽车的人的结果。所以以下内容应该有效:
PersonQuery::create()
->withColumn('Possession.possession_name', 'CarName')
->usePossessionQuery()
->filterByPossessionType('car')
->endUse()
->find()
->toString();
useXxxQuery()也可以给别名,所以你应该能够完成相同的操作,但更容易:)
编辑:根据您的第二个示例,具有多个联接的示例:
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();
答案 1 :(得分:2)
找到此问题的解决方法。问题是,当我尝试对同一个表执行多个左连接时(通过使用别名),Propel还包括到该表的交叉连接。但是,我发现如果第一个连接没有使用别名,则Propel不会添加交叉连接。
所以,总而言之,这不起作用:
$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;
但这符合预期:
$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;
答案 2 :(得分:0)
这绝对看起来像个错误。你有举报吗?
通常,Propel会检查标准所指向的关系/别名,然后确保所有这些表都包含在查询中。如果该关系/别名没有现有的连接子句,则它将包含CROSS JOIN。
->where()
方法没有通过方法参数提供的关系别名(比如filterByX有),而推进时看到'p'。 where()条件的一部分映射到拥有表,它没有意识到它通过使用关系别名得到了这个结论(这是错误),所以propel认为where()想要检查对possession
表(没有别名),因此包括交叉连接。
因为这个错误,你的解决方法才有效,写起来会更清楚:
->where('possession.possession_name IS NOT NULL')
...澄清那里where()指的是不公平的占有表而不是特定的关系别名。
如果我是正确的,如果您引用了第二个别名,则您的解决方法不起作用:
->where('p2.possession_name IS NOT NULL')
(它仍会引用第一个关系)
PS Imho,Propel应该抛出异常而不是这种自动交叉连接 - “修复”查询,因为大多数情况下,交叉连接是拼写错误或查询方法使用不正确的不必要结果。与此同时,这里有一个带有帮助代码的要点,让您检查这些条件并在必要时抛出异常:https://gist.github.com/motin/2b00295ca71bb876f9873712544dd077