DQL выберите все строки, имеющие максимальное значение для одного столбца
Работая с 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
Кто-нибудь знает, как сделать это в доктрине 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();
Сущность называется GAAffairesBundle: MontantMarche, поэтому этот код находится в методе соответствующего репозитория.
В более общем плане я изучаю, как обрабатывать подзапросы (SQL & DQL) и синтаксис DQL для advanced запросы.
ТХ!
3 ответа:
После нескольких часов головной боли, поиска в гугле и чтения 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
, а другая настройка в join-это я попытка объединить только строки из правой таблицы(b)
, где a-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
Надеюсь, это имеет смысл