作者:asdfu_814 | 来源:互联网 | 2024-11-27 13:27
本文源自先前关于数据库查询的问题讨论。最近,我将项目升级到了 Propel 1.5,并开始利用其新的 Query 功能替代传统的 Criteria 方法。然而,在处理一个特定的查询时遇到了挑战——这个查询需要基于多个条件进行左连接:
SELECT * FROM person
LEFT JOIN group_membership ON
person.id = group_membership.person_id
AND group_id = 1
WHERE group_membership.person_id IS NULL;
此查询的目标是找出所有未加入特定组的人员。在升级之前,我是通过以下方式实现这一目标的:
$criteria->addJoin(array(
self::ID,
GroupMembershipPeer::GROUP_ID,
), array(
GroupMembershipPeer::PERSON_ID,
$group_id,
),
Criteria::LEFT_JOIN);
$criteria->add(GroupMembershipPeer::PERSON_ID, null, Criteria::EQUAL);
考虑到另一种解决方案,即先查询特定组内的所有成员,然后使用 NOT IN 条件排除这些成员,但这不仅效率低下,且实现起来不够优雅。
在查阅了 codenugget.org 上的相关文章后,我尝试了如下方法来为连接添加额外条件:
$result = $this->leftJoin('GroupMembership');
$result->getJoin('GroupMembership')->addCondition(GroupMembershipPeer::GROUP_ID, $group->getId());
return $result->useGroupMembershipQuery()->filterByPersonId(null)->endUse();
然而,这种方法的问题在于 useGroupMembershipQuery()
会覆盖之前的左连接设置。为了解决这一问题,我进行了进一步的尝试:
$result = $this->useGroupMembershipQuery('GroupMembership', Criteria::LEFT_JOIN)->filterByPersonId(null)->endUse();
$result->getJoin('GroupMembership')->addCondition(GroupMembershipPeer::GROUP_ID, $group->getId());
return $result;
尽管如此,这段代码最终导致了一个交叉连接(CROSS JOIN)的发生,而非预期的左连接:
SELECT * FROM `person`
CROSS JOIN `group_membership`
LEFT JOIN group_membership GroupMembership ON
(person.ID=GroupMembership.PERSON_ID
AND group_membership.GROUP_ID=3)
WHERE group_membership.PERSON_ID IS NULL
对于为何会出现这种情况,以及如何在 Propel 1.5 中正确地执行此类连接查询,希望得到社区的帮助。
解决方案:
Propel 1.6 引入了对多条件连接的支持,通过 addJoinCondition()
方法。如果您能够更新 Symfony 插件或迁移到 sfPropelORMPlugin,那么可以采用以下更加简洁的查询方式:
return $this->leftJoin('GroupMembership')->addJoinCondition('GroupMembership', 'GroupMembership.GroupId = ?', $group->getId())->where('GroupMembership.PersonId IS NULL');