教条2:限制左联/分页 - 最佳实践

时间:2012-05-22 12:10:46

标签: pagination doctrine-orm subquery database-performance query-builder

我有一个大查询(在我的查询构建器中)和很多左连接。所以我得到他们的评论和标签等文章。 假设我有以下dql:

$dql = 'SELECT blogpost, comment, tags 
FROM BlogPost blogpost 
LEFT JOIN blogpost.comments comments
LEFT JOIN blogpost.tags tags';

现在让我们说我的数据库有超过100个博客帖子,但我只想要前10个,但是包含这10个及其所有标签的所有评论,如果它们存在的话。 如果我使用setMaxResults它限制行。所以我可能得到前两个帖子,但最后一个帖子缺少一些评论或标签。所以跟随不起作用。

$result = $em->createQuery($dql)->setMaxResults(15)->getResult();

使用doctrine2.2附带的几乎没有文件记录的Pagination解决方案对我来说并不适用,因为它太慢了,我也可以加载所有数据。

我尝试了Stackoverflow Article中的解决方案,但即使该文章仍然缺少最佳实践,所提出的解决方案也非常慢。

关于如何做到这一点,是否有最佳做法? 没有人在生产模式下使用Doctrine2.2吗?

2 个答案:

答案 0 :(得分:11)

使用这样的查询获得正确的结果是有问题的。 Doctrine网站上有一个教程解释了这个问题。

Pagination

本教程更多的是关于分页而不是获得前5个结果,但总的想法是你需要做一个“SELECT DISTINCT a.id FROM articles a ... LIMIT 5”而不是普通的SELECT。这比这复杂一点,但该教程的最后两点应该让你走上正轨。

<强>更新

这里的问题不是Doctrine或任何其他ORM。问题在于数据库能够返回您要求的结果。这就是加入工作的方式。

如果您对查询执行EXPLAIN,它将为您提供有关正在发生的事情的更深入的答案。将结果添加到您的初始问题中是个好主意。

根据分页文章中讨论的内容,您似乎需要至少2个查询才能获得所需的结果。将DISTINCT添加到查询中可能会大大减慢查询速度,但只有在您加入查询时才真正需要它。您可以编写另一个查询,只检索按创建日期排序的前10个帖子,而不加入连接。获得这10个帖子的ID后,请使用您的联接进行另一个查询,然后使用WHERE blogpost.id IN (...) ORDER BY blogpost.created。这种方法应该更有效率。

SELECT 
    bp 
FROM 
    Blogpost bp 
ORDER BY 
    bp.created DESC
LIMIT 10

由于您在第一个查询中所关心的只是ID,因此您可以将Doctrine设置为使用Scalar Hydration。

SELECT 
    bg 
FROM 
    Blogpost bp 
LEFT JOIN 
    bp.comments c 
LEFT JOIN 
    bp.tags t 
WHERE 
    bp.id IN (...) 
ORDER BY 
    bp.created DESC

您也可以使用相关子查询在一个查询中执行此操作。子查询总是坏的神话不是真的。有时它们比连接更快。您需要尝试找出最适合您的解决方案。

答案 1 :(得分:2)

根据澄清的问题

修改

您可以使用FROM子句中的子查询在本机MySQL中执行您想要的操作:

SELECT * FROM 
 (SELECT * FROM articles ORDER BY date LIMIT 5) AS limited_articles, 
 comments, 
 tags 
WHERE
 limited_articles.article_id=comments.article_id
 limited_articles.article_id=tags.article_id

据我所知,DQL不支持这样的子查询,因此您可以使用NativeQuery类。