当前位置: 首页 > 面试题库 >

使用Doctrine QueryBuilder从Select语句获得的内部联接结果

暴博远
2023-03-14
问题内容

您是否可以将Doctrine QueryBuilder用于包含一个INNER JOIN完整SELECT语句的临时表中GROUP BY

最终目标是选择记录的最佳版本。我有一个viewVersion表,它具有具有相同viewId值但不同timeMod的多个版本。我想找到具有最新timeMod的版本(并对查询执行许多其他复杂的联接和过滤器)。

最初,人们认为您可以先执行a GROUP BY viewId,然后执行ORDER BY timeMod,但是ORDER BY对GROUP
BY无效,MySQL将返回随机结果。有大量的答案(例如在这里)来解释使用GROUP的问题并提供解决方案,但是我很难解释Doctrine文档以找到使用Doctrine
QueryBuilder实现SQL的方法(如果可能的话)
。我为什么不只使用DQL?我可能必须这样做,但是我有很多动态过滤器和联接,使用QueryBuilder可以轻松得多,所以我想看看是否有可能。

在MySQL QueryBuilder中复制的示例MySQL

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

该答案似乎表明在IN语句等其他上下文中也是可能的,但是当我在JOIN中尝试上述方法时,出现错误:

[Semantical Error] line 0, col 90 near '(SELECT MAX(v.timeMod)': Error: Class '(' is not defined.

问题答案:

非常感谢@AdrienCarniero提供的替代查询结构,该查询结构使用简单的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;

使用教义QueryBuilder

$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();

在性能方面,它实际上取决于数据集。有关详细信息,请参见此讨论。

提示:该表应在这两个值(viewId和timeMod)上都包含索引,以加快结果的速度。 我不知道这是否也会从两个领域的单一索引中受益。

在某些情况下,使用原始JOIN方法的本机SQL查询 可能
会更好,但使用动态创建它的扩展代码范围编译该查询,并使映射正确将是一件痛苦的事情。因此,这至少是一种替代解决方案,希望对其他人有所帮助。



 类似资料:
  • 问题内容: 我有一个在MySQL中运行良好的查询,但是在Oracle上运行该查询时,出现以下错误: SQL错误:ORA-00933:SQL命令未正确终止 00933。00000-“ SQL命令未正确终止” 查询是: 问题答案: 该语法在Oracle中无效。你可以这样做: 或者您 可以 执行以下操作: 它取决于内联视图是否被Oracle更新( 要更新第二条语句取决于 此处 列出的一些规则 )。

  • 问题内容: 我不知道是什么问题。使用MySQL 5.0尝试运行以下MYSQL更新语句时出现编译错误 所有字段名称都是正确的。有什么想法吗? 问题答案: 尝试这个: 更新: 既然您说查询产生了语法错误,所以我创建了一些可以对其进行测试的表,并确认查询中没有语法错误: 看到?没有语法错误。我针对MySQL 5.5.8进行了测试。

  • 我的目标是将下面的查询转换为使用内连接。 我尝试过这样的东西,但似乎不对: 我的任务是将第一个查询转换为使用内部联接语法。

  • 当运行多个内部连接时,我得到的结果为零 代码如下: 我知道这两个表中都有信息,这是奇怪的,它没有产生结果。 非常感谢任何帮助。

  • 我试图在一列内连接两个表。从数据库方面来看,没有映射,因为这是我不想讨论的事情。 我想使用内部联接执行HQL查询并检索角色对象/结果。 这是到目前为止我的hql 一场createQuery(“从角色作为角色内部加入参与作为角色上的参与。id=invention.roleid,其中invention.id=X”)。list(); 我看到在HQL上不可用。如何明确地告诉Hibernate仅加入此列。

  • 问题内容: 这个问题已经在这里有了答案 : INNER JOIN ON vs WHERE子句 (12个答案) 7年前关闭。 我有两个选择连接SQL语句: 显然,它们的结果相同。但是它们之间没有任何区别,例如性能,可移植性。 问题答案: 一个区别是,第一个选项通过在where子句中表达联接条件来隐藏意图。 第二个选项,写出连接条件的条件对于阅读查询的用户来说更加清楚。它显示了查询的确切意图。 至于性