2

我正在尝试使用 Doctrine 的查询构建器创建一个非常复杂的查询(我使用的是 Doctrine 2.2)。在模型中,我有一个Distributor类和一个DistributorVisit具有一对多关系的类。每次代表访问经销商时,都会在DistributorVisit表格中添加一个带有访问日期的新行。可以在此处找到这两个表的 ER 图。

现在我希望能够按他们的最后访问日期过滤分销商。因此,用户输入一个日期范围(上次访问时间和上次访问时间),最后访问日期在这两个日期之间的经销商会被列出。我正在使用 Doctrine 的查询生成器,因为我在过滤器中执行了许多其他条件查询,并且我发现面向对象的方法在这种情况下效果最好。这是我在DistributorRepository课堂上所做的:

$qb = $this->getEntityManager()->createQueryBuilder()
     ->select('o')
     ->from('MyBundle:Distributor', 'o');

// Lots of 'andWhere's here

$qbv = $this->getEntityManager()->createQueryBuilder();

$qb->andWhere($qb->expr()->between(

    $qbv->select($qbv->expr()->max('v.visitDate'))
        ->from('MyBundle:DistributorVisit', 'v')
        ->join('MyBundle:Distributor', 'o2',
            Join::WITH,
            $qbv->expr()->andX(
                $qbv->expr()->eq('o2.id', 'v.distributorId'),
                $qbv->expr()->eq('o2.id', 'o.id')
            ))
        ->getDQL(),

    $filter->getLastVisitFrom()->getTimestamp(),
    $filter->getLastVisitTo()->getTimestamp()
));

这给了我以下错误:

[Syntax Error] line 0, col 83: Error: Expected Literal, got 'SELECT'

我想这是因为查询生成器需要一个文字,我的子选择是,但是,子查询的结果应该是一个文字,对吧?可能是因为查询生成器没有相应地添加括号?

非常感谢你的帮助。

4

3 回答 3

5

我现在通过以下方式解决了这个问题:

$qb = $this->getEntityManager()->createQueryBuilder()
    ->select('o')
    ->from('MyBundle:Distributor', 'o');

$qbdv = $this->getEntityManager()->createQueryBuilder();
$qbdv->select('MAX(dv2.visitDate)')
    ->from('MyBundle:DistributorVisit', 'dv2')
    ->where($qbdv->expr()->eq('dv2.distributor', 'o'));

$maxVisitDate = '('.$qbdv->getDQL().')';

$qb->leftJoin(
    'o.distributorVisits',
    'dv',
    Join::WITH,
    $qb->expr()->eq('dv.visitDate', $maxVisitDate)
);

$qb->andWhere(
    $qb->expr()->between(
        'dv.visitDate',
        ':dateFrom',
        ':dateTo'
    )
)
->setParameter('dateFrom', $filter->getLastVisitFrom())
->setParameter('dateTo', $filter->getLastVisitTo());

所以我基本上做的是以下几点:我将DistributorVisit表加入到Distributor最大访问日期的表中。诀窍在于可以将(子)查询($qb1->getDQL())的 DQL 直接传递给 Doctrine 表达式($qb2->expr()->eq('column', $qb1->getDQL())。我在上面的代码中使用左连接来做到这一点。

于 2012-11-20T20:43:20.347 回答
1

我猜您当前的 DQL 如下所示:

SELECT ..
FROM ..
WHERE SELECT .. FROM .. BETWEEN .. AND ..

但应该是这样的:

SELECT ..
FROM ..
WHERE (SELECT .. FROM ..) BETWEEN .. AND ..

要修复您的代码,我只需将子查询的 dql 放在括号内:

$subQueryDQL = $qbv->select($qbv->expr()->max('v.visitDate'))
    ->from('MyBundle:DistributorVisit', 'v')
    ...
    ->getDQL();

$qb->andWhere($qb->expr()->between(
    sprintf('(%s)', $subQueryDQL),
    $filter->getLastVisitFrom()->getTimestamp(),
    $filter->getLastVisitTo()->getTimestamp()
));
于 2012-09-27T08:16:21.683 回答
0

我的情况:

$qb->andWhere(
"t.field BETWEEN (
    {$subQuerybuilder1->getDQL()}
  ) AND (
    {$subQuerybuilder2->getDQL()}
  )
");

得到:

SELECT ... WHERE t.field BETWEEN (
        SELECT t1.field FROM t1
      ) AND (
        SELECT t2.field FROM t2
      )
于 2018-03-06T13:53:07.037 回答