2

DoctrineのQueryBuilderを使用して非常に複雑なクエリを作成しようとしています(Doctrine 2.2を使用しています)。モデルには、DistributorクラスとDistributorVisit1対多の関係を持つクラスがあります。ディストリビューターが担当者によって訪問されるたびにDistributorVisit、訪問日とともに新しい行がテーブルに追加されます。2つのテーブルのER図はここにあります。

ここで、最後の訪問日でディストリビューターをフィルタリングできるようにしたいと思います。したがって、ユーザーは日付範囲(最後の訪問元と最後の訪問)を入力し、最後の訪問日がこれら2つの日付の間にあるディストリビューターがリストされます。フィルターで行う条件付きクエリが他にもたくさんあるため、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 に答える