您可以使用Doctrine QueryBuilder来INNER JOIN
来自包含SELECT
的完整GROUP BY
语句的临时表吗?
最终目标是选择最佳版本的唱片。我有一个viewVersion表,有多个版本具有相同的viewId值但不同的timeMod。我想找到具有最新timeMod的版本(并在查询上执行许多其他复杂的连接和过滤)。
最初人们假设您可以执行GROUP BY viewId
然后ORDER BY timeMod
,但ORDER BY对GROUP BY没有影响,MySQL会返回随机结果。有很多答案(例如here)解释了使用GROUP并提供解决方案的问题,但我在解释Doctrine文档时找不到使用Doctrine QueryBuilder实现SQL的方法(如果它甚至可能)。为什么我不使用DQL?我可能不得不这样做,但我有很多动态过滤器和连接,使用QueryBuilder更容易,所以我想看看是否可能。
SELECT vv.*
FROM view_version vv
#inner join only returns where the result sets overlap, i.e. one record
INNER JOIN (
SELECT MAX(timeMod) maxTimeMod, viewId
FROM view_version
GROUP BY viewId
) version ON version.viewId = vv.viewId AND vv.timeMod = version.maxTimeMod
#join other tables for filter, etc
INNER JOIN view v ON v.id = vv.viewId
INNER JOIN content_type c ON c.id = v.contentTypeId
WHERE vv.siteId=1
AND v.contentTypeId IN (2)
ORDER BY vv.title ASC;
我认为JOIN需要注入一个DQL语句,例如
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryMax = $viewVersionRepo->createQueryBuilder()
->addSelect('MAX(timeMod) AS timeModMax')
->addSelect('viewId')
->groupBy('viewId');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// I tried putting the query in a parenthesis, to no avail
->join('('.$queryMax->getDQL().')', 'version', 'WITH', 'vv.viewId = version.viewId AND vv.timeMod = version.timeModMax')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
我的代码(可能与上述示例完全不符)输出:
SELECT e, view, contentType
FROM Gutensite\CmsBundle\Entity\View\ViewVersion e
INNER JOIN (
SELECT MAX(v.timeMod) AS timeModMax, v.viewId
FROM Gutensite\CmsBundle\Entity\View\ViewVersion v
GROUP BY v.viewId
) version WITH vv.viewId = version.viewId AND vv.timeMod = version.timeModMax
INNER JOIN e.view view
INNER JOIN view.contentType contentType
WHERE e.siteId = :siteId
AND view.contentTypeId IN (:contentTypeId)
ORDER BY e.title ASC
This Answer似乎表明它可能在IN
语句之类的其他上下文中,但是当我在JOIN中尝试上述方法时,我收到错误:
[Semantical Error] line 0, col 90 near '(SELECT MAX(v.timeMod)': Error: Class '(' is not defined.
答案 0 :(得分:2)
非常感谢@AdrienCarniero为他的alternative query structure用最简单的JOIN排序最高版本,其中实体的timeMod小于连接的表timeMod。
SELECT view_version.*
FROM view_version
#inner join to get the best version
LEFT JOIN view_version AS best_version ON best_version.viewId = view_version.viewId AND best_version.timeMod > view_version.timeMod
#join other tables for filter, etc
INNER JOIN view ON view.id = view_version.viewId
INNER JOIN content_type ON content_type.id = view.contentTypeId
WHERE view_version.siteId=1
# LIMIT Best Version
AND best_version.timeMod IS NULL
AND view.contentTypeId IN (2)
ORDER BY view_version.title ASC;
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// Join Best Version
->leftJoin('GutensiteCmsBundle:View\ViewVersion', 'bestVersion', 'WITH', 'bestVersion.viewId = e.viewId AND bestVersion.timeMod > e.timeMod')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
// LIMIT Joined Best Version
->andWhere('bestVersion.timeMod IS NULL')
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
就性能而言,它实际上取决于数据集。 See this discussion for details。
提示:该表应包含这两个值(viewId和timeMod)的索引,以加快结果。我不知道它是否也会从两个字段的单个索引中受益。
使用原始JOIN方法的本机SQL查询在某些情况下可能更好,但是在动态创建它的扩展代码范围内编译查询,并且使映射正确是一种痛苦。所以这至少是一种替代解决方案,我希望能帮助其他人。