doctrine dql where子句与关联实体的解释

doctrine dql where clause interpretation with associated entity

给定这个实体

class SystemRecord
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer", name="ID")
     * @ORM\GeneratedValue
     * @var int
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="Application\Entity\User")
     * @ORM\JoinColumn(name="USER_SERIAL", referencedColumnName="SERIAL", nullable=false)
     * @var User
     */
    private $user;

    /**
     * @ORM\Column(type="utcdatetime", name="DATE_DATA_WAS_FETCHED", nullable=false)
     * @var DateTimeInterface
     */
    private $dateDataWasFetched;
}

...和这个 dql

$dql = "
    select r
      from Application\Entity\SystemRecord r
      join Application\Entity\User u
     where r.dateDataWasFetched = (
         select max(r2.dateDataWasFetched)
           from Application\Entity\SystemRecord r2
     )
       and u.serial = :serial
";

$query = $this->getEntityManager()->createQuery($dql);
$query->setParameter('serial', $user->getSerial());
$sql = $query->getSql();

...我希望得到"the SystemRecords for the user with the specified serial, but only those with the most recent date out of any SystemRecord"。换句话说,在程序上,"find the most recent date of any SystemRecord for any user. Then find records for the specified user which occurred on that date."

如果我写sql,我会写

select *
  from SYSTEM_RECORDS r
  join USER u
    on r.USER_SERIAL = u.SERIAL
 where DATE_DATA_WAS_FETCHED = (select max(DATE_DATA_WAS_FETCHED) from SYSTEM_RECORDS)
   and u.SERIAL = ?

但是,学说给了我以下内容 sql

SELECT ...fields from s0_ ...
  FROM SYSTEM_RECORDS s0_ 
 INNER 
  JOIN 
  USER u1_
   ON (s0_.DATE_DATA_WAS_FETCHED = (SELECT max(s2_.DATE_DATA_WAS_FETCHED) AS dctrn__1
                                     FROM SYSTEM_RECORDS s2_) AND u1_.SERIAL = ?)

这不是我想要的。这给了我 "SystemRecords for all users whose SystemRecords have the same date as the most recent SystemRecords for the user with the specified serial".

如何使用 dql 制定查询?


我能够通过避免连接solve/avoid 我的问题

$dql = "
    select r
      from Application\Entity\SystemRecord r
     where r.dateDataWasFetched = (
         select max(r2.dateDataWasFetched)
           from Application\Entity\SystemRecord r2
     )
       and r.user = :user
";

$query = $this->getEntityManager()->createQuery($dql);
$query->setParameter('user', $user);

结果sql(正确)

SELECT ...fields from s0_ ...
FROM SYSTEM_RECORDS s0_
WHERE s0_.DATE_DATA_WAS_FETCHED = (SELECT max(s1_.DATE_DATA_WAS_FETCHED) AS dctrn__1
                                   FROM SYSTEM_RECORDS s1_) AND s0_.USER_SERIAL = ?

显着的区别在于,我现在指定实体本身(通过 r.user = :user),而不是指定关联实体的 ID(通过 u.serial = :serial)。这允许我省略也加入。顺便说一句 - serial 字段在我的 User 实体中标记为 @ORM\Id

不过,这只是在回避问题。我仍然对 doctrine 在存在连接时如何解释查询感到困惑。

编辑 - 找到真正的解决方案

感谢 Wilt,在使用查询生成器然后使用 getDQL() 方法后,我找到了缺少的细节。工作 dql 是

select r
  from Application\Entity\SystemRecord r
  join r.user u
 where r.dateDataWasFetched = (
     select max(r2.dateDataWasFetched)
       from Application\Entity\SystemRecord r2
 )
   and u.serial = :serial

请注意,我原来问题中的 DQL 与此有效解决方案之间的区别分别是 join Application\Entity\User ujoin r.user u

如果我理解正确,您需要像您一样使用子查询,但我认为您缺少 in 表达式。使用 QueryBuilder 您将构建查询以获得这样的结果(我总是使用 QueryBuilder 编写查询):

$qb->select(r)
   ->from('SystemRecord', 'r')
   ->join('r.user', 'u')
   ->where(
       $qb->expr()->in(
           'r.dateDataWasFetched',
           "SELECT max(r2.dateDataWasFetched) FROM Application\Entity\SystemRecord r2"
       )
   )
   ->andWhere('u.serial' = :user_serial)
   ->setParameter('user_serial', $user->getSerial())
   ->getQuery()
   ->getResult();

此答案基于 this answer to similar question on stackoverflow

编辑:

如果您确实需要 DQL,那么您可以在使用 getDQL 方法构建查询后轻松地从 QueryBuilder 实例中获取它,如下所示:

$dql = $qb->getQuery()->getDQL();