在Doctrine 2中执行WHERE .. IN子查询

chi*_*org 56 sql database doctrine-orm

我想从具有特定商品的所有订单中选择订单商品.在SQL中我会这样做:

SELECT DISTINCT i.id, i.name, order.name 
FROM items i 
JOIN orders o ON i.order_id=o.id 
WHERE o.id IN (
   SELECT o2.id FROM orders o2
   JOIN items i2 ON i2.order_id=o2.id AND i2.id=5
)
AND i.id != 5
ORDER BY o.orderdate DESC
LIMIT 10
Run Code Online (Sandbox Code Playgroud)

如何使用查询构建器执行此查询?

fak*_*ken 101

我就是这样尝试的:

/** @var Doctrine\ORM\EntityManager $em */
$expr = $em->getExpressionBuilder();
$em->createQueryBuilder()
   ->select(array('DISTINCT i.id', 'i.name', 'o.name'))
   ->from('Item', 'i')
   ->join('i.order', 'o')
   ->where(
       $expr->in(
           'o.id',
           $em->createQueryBuilder()
               ->select('o2.id')
               ->from('Order', 'o2')
               ->join('Item', 
                      'i2', 
                      \Doctrine\ORM\Query\Expr\Join::WITH, 
                      $expr->andX(
                          $expr->eq('i2.order', 'o2'),
                          $expr->eq('i2.id', '?1')
                      )
               )
               ->getDQL()
       )
   )
   ->andWhere($expr->neq('i.id', '?2'))
   ->orderBy('o.orderdate', 'DESC')
   ->setParameter(1, 5)
   ->setParameter(2, 5)
   ;
Run Code Online (Sandbox Code Playgroud)

我当然没有对此进行测试,并对您的模型做了一些假设.可能的问题:

  • 限制:这在Doctrine 2中有点问题,看起来查询构建器并不是很擅长接受限制.请看这里,这里这里.
  • IN子句通常与数组一起使用,但我认为它将与子查询一起使用.
  • 你可能可以使用相同的参数?1,而不是两个参数(因为它们是相同的值),但我不确定.

总而言之,这可能不适用于第一次,但肯定会让你走上正轨.之后告诉我们最终的100%正确答案.

  • 请注意,示例中使用了两个querybuilder.这应该可以节省下一个人的时间...... (7认同)
  • 感谢您的更正.对于使用Doctrine 2 QueryBuilder的每个人来说,这将是一个非常有用的参考.最好的祝福 (6认同)
  • 谢谢!您的示例中只缺少两个内容:Namespace在开头需要一个反斜杠,子查询需要使用getDQL()方法作为字符串给出.我已编辑您的示例以更正此问题 (4认同)
  • 如果内部查询具有一些绑定参数,这可能是危险的,因为在我的经验中调用getDql()会使任何此类绑定无效.因此,需要在外部查询中再次使用setParameter绑定参数,否则代码将以"无效参数编号:绑定变量数与令牌数不匹配"中断. (4认同)

小智 9

只是为了避免混淆clang1234发布的最后评论.

dql查询示例确实有效.确实,expr-> in()会将第二个参数强制转换为数组,在本例中为dql字符串.它做什么,它只是创建一个以dql查询字符串作为第一个元素的数组.这正是Expr\Func正在等待的数组.在Doctrine 2代码中更深入一点,dql查询字符串数组元素将被正确管理.(有关详细信息,请参阅DBAL/Platforms/AbstractPlatform.php方法getInExpression,数组内爆入IN())