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 u
和 join 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();
给定这个实体
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 u
和 join 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();