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

时间:2015-05-04 19:00:37

标签: php sql doctrine-orm dql

鉴于此实体

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();

...我希望得到具有指定序列号的用户的SystemRecords,但只有那些具有最新日期的SystemRecord"。换句话说,在程序上,"为任何用户找到任何SystemRecord的最新日期。然后查找在该日期发生的指定用户的记录。"

如果我正在写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与具有指定序列号的用户的最新SystemRecords具有相同日期的所有用户,这给了我" SystemRecords。

如何使用dql制作查询?

2 个答案:

答案 0 :(得分:2)

如果我理解正确,你需要像你一样使用子查询,但我认为你错过了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,那么在使用QueryBuilder方法构建查询之后,您可以轻松地从getDQL实例中获取它:

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

答案 1 :(得分:1)

我能够通过避免加入来解决/避免我的问题

$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 = ?

值得注意的是,我不是指定关联实体的ID(通过u.serial = :serial,而是指定实体本身(通过r.user = :user)。这样我就可以省略也加入。顺便说一下 - serial字段在@ORM\Id实体中标有User

然而,这只是避免了这个问题。我仍然感到困惑的是,当存在联接时,教条如何解释查询。

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

感谢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 u vs join r.user u