【问题标题】:doctrine dql where clause interpretation with associated entity原则 dql where 子句与关联实体的解释
【发布时间】:2015-07-14 07:10:15
【问题描述】:

给定这个实体

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

这不是我想要的。这给了我“系统记录与具有指定序列的用户的最新系统记录具有相同日期的所有用户的系统记录”。

如何使用 dql 制定我的查询?


【问题讨论】:

  • 您如何在dql 中定义应该使用哪些字段来连接两个表?它是否应该以某种方式隐含地得出这些信息?在您的dql 文本中,我没有看到任何提及r 应与u 加入r.user_serial = u.serial
  • 无论如何,如果您将and r.user_serial = :serialand r.user_serial = u.serial 添加到dql 文本的where 部分,您应该会得到产生正确结果的SQL 查询。
  • @VladimirBaranov 我想教义使用带有@Id 注释的字段来确定加入实体关联时要使用的主键。我无法添加您建议的添加内容,因为该实体没有名为 user_serial 的字段,因此教义会引发错误。

标签: php sql doctrine-orm dql


【解决方案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 = ?

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

但是,这只是在避免问题。当存在连接时,我仍然对教义如何解释查询感到困惑。

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

感谢 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

【讨论】:

    【解决方案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,那么您可以在使用getDQL 方法构建查询后轻松地从QueryBuilder 实例中获取它,如下所示:

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

    【讨论】:

    • 这行得通。我真的很想知道如何通过 dql 做到这一点。如果我最终没有得到答案,我会接受这个,现在会投票。谢谢!
    • @goat 为什么你能得到DQL?使用Querybuilder 更灵活。但是您可以轻松地从您的QueryBuilder 实例中获取DQL。我将编辑我的答案,向您展示如何...
    • 我想知道 dql 主要是出于好奇——我想我喜欢那种让我想起 sql 的熟悉而简洁的声明式风格。无论如何,getDQL() 方法帮助我解决了它,请参阅我编辑的答案。谢谢!
    猜你喜欢
    • 2011-11-01
    • 2021-08-13
    • 1970-01-01
    • 1970-01-01
    • 2011-12-31
    • 1970-01-01
    • 1970-01-01
    • 2014-11-07
    • 1970-01-01
    相关资源
    最近更新 更多