Работая с Symfony 2 и Doctrine, я ищу способ выбрать все строки, имеющие максимальное значение в определенном столбце.
Прямо сейчас я делаю это в двух запросах:
Я уверен, что это можно сделать одним запросом.
В поисках я нашел этот ответ в Тема, кажется, это то, что я ищу, но в SQL.
Итак, согласно первому решению ответа, запрос, который я пытаюсь построить, будет выглядеть примерно так:
select yt.id, yt.rev, yt.contents
from YourTable yt
inner join(
select id, max(rev) rev
from YourTable
group by id
) ss on yt.id = ss.id and yt.rev = ss.rev
Кто-нибудь знает, как это сделать в Doctrine DQL?
На данный момент вот код для моих тестов (не работает):
$qb2= $this->createQueryBuilder('ms')
->select('ms, MAX(m.periodeComptable) maxPeriode')
->where('ms.affaire = :affaire')
->setParameter('affaire', $affaire);
$qb = $this->createQueryBuilder('m')
->select('m')
//->where('m.periodeComptable = maxPeriode')
// This is what I thought was the most logical way of doing it:
->innerJoin('GAAffairesBundle:MontantMarche mm, MAX(mm.periodeComptable) maxPeriode', 'mm', 'WITH', 'm.periodeComptable = mm.maxPeriode')
// This is a version trying with another query ($qb2) as subquery, which would be the better way of doing it for me,
// as I am already using this subquery elsewhere
//->innerJoin($qb2->getDQL(), 'sub', 'WITH', 'm.periodeComptable = sub.maxPeriode')
// Another weird try mixing DQL and SQL logic :/
//->innerJoin('SELECT MontantMarche mm, MAX(mm.periodeComptable) maxPeriode ON m.periodeComptable = mm.maxPeriode', 'sub')
//->groupBy('m')
->andWhere('m.affaire = :affaire')
->setParameter('affaire', $affaire);
return $qb->getQuery()->getResult();
Entity — GAAffairesBundle: MontantMarche, поэтому этот код находится в методе соответствующего репозитория.
В более общем плане я учусь тому, как обрабатывать подзапросы (SQL & DQL) и синтаксис DQL для сложных запросов.
Спасибо!
После нескольких часов головной боли, поисков в Google и показаний stackOverflow …
Я наконец узнал, как это сделать.
Вот мой последний код DQL queryBuilder:
$qb = $this->createQueryBuilder('a');
$qb2= $this->createQueryBuilder('mss')
->select('MAX(mss.periodeComptable) maxPeriode')
->where('mss.affaire = a')
;
$qb ->innerJoin('GAAffairesBundle:MontantMarche', 'm', 'WITH', $qb->expr()->eq( 'm.periodeComptable', '('.$qb2->getDQL().')' ))
->where('a = :affaire')
->setParameter('affaire', $affaire)
;
return $qb->getQuery()->getResult();
Для меня, когда я пытаюсь сделать подзапрос, я делаю:
->andWhere($qb->expr()->eq('affaire', $qb2->getDql()));
Чтобы достичь этого, используя чистый DQL и без использования какой-либо агрегатной функции, вы можете написать доктрину как
SELECT a
FROM GAAffairesBundle:MontantMarche a
LEFT JOIN GAAffairesBundle:MontantMarche b
WITH a.affaire = b.affaire
AND a.periodeComptable < b.periodeComptable
WHERE b.affaire IS NULL
ORDER BY a.periodeComptable DESC
Вышеприведенное вернет вам максимальную запись на группу affaire
)
Expalnation
Эквивалентный SQL для вышеупомянутого DQL будет похож
SELECT a.*
FROM MontantMarche a
LEFT JOIN MontantMarche b
ON a.affaire = b.affaire
AND a.periodeComptable < b.periodeComptable
WHERE b.affaire IS NULL
ORDER BY a.periodeComptable DESC
Здесь я предполагаю, что может быть несколько записей в таблице, например, (MontantMarche) для каждого affaire
так что здесь я пытаюсь сделать сам присоединиться к affaire
и еще один трюк в соединении, я пытаюсь объединить только строки из правой таблицы(b)
где Период < b’s periodeComptable, поэтому строка для левой таблицы (a)
с самым высоким periodeComptable
будет иметь нулевую строку из правой таблицы(b)
таким образом, чтобы выбрать самый высокий ряд за affaire
WHERE right table row IS NULL
необходимо.
Точно так же используя ваш отправленный пример запроса с внутренним объединением можно записать как
select yt.id, yt.rev, yt.contents
from YourTable yt
left join YourTable ss on yt.id = ss.id and yt.rev < ss.rev
where ss.rev is null
Надеюсь, это имеет смысл