首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Pagerfanta与Doctrine2计数优化

Pagerfanta与Doctrine2计数优化
EN

Stack Overflow用户
提问于 2014-04-18 17:22:01
回答 2查看 2.8K关注 0票数 6

我正在使用Symfony2和Silex的Pager新鲜和Doctrine。随着我的数据库变得越来越大,我注意到管理统计页面上的巨大负载,这些页面显示带有分页的大数据。我检查了分析器,看到了令人难以置信的低效查询:

代码语言:javascript
复制
SELECT DISTINCT id16
FROM (
    SELECT f0_.username AS username0, ..., f0_.added_on AS added_on20
    FROM fos_user f0_ ORDER BY f0_.id DESC
) dctrn_result
LIMIT 50 OFFSET 0;

SELECT COUNT(*) AS dctrn_count
FROM (
    SELECT f0_.username AS username0, ..., f0_.added_on AS added_on20
    FROM fos_user f0_ ORDER BY f0_.id DESC
) dctrn_result
LIMIT 50 OFFSET 0;`

第一个查询很容易通过创建DoctrineORMAdapter类的固定版本来修复。生成COUNT()查询的代码更复杂,因此我决定询问是否有任何解决方案。

那么,有什么方法可以让Pager凡塔不运行嵌套查询呢?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-07-01 10:59:55

迟到总比从来没有好:我今天撞到了同一堵墙,有超过200 K的记录,并找到了解决办法。

Pagerfanta内部使用Doctrine\ORM\Tools\Pagination\CountOutputWalker来计数对象,这将导致这样的计数查询:

代码语言:javascript
复制
SELECT 
  COUNT(*) AS dctrn_count 
FROM 
  (
    SELECT 
      DISTINCT id_0 
    FROM 
      (
        SELECT 
          m0_.id AS id_0, 
          ...
        FROM 
          messaging_messages m0_ 
        ORDER BY 
          m0_.id DESC
      ) dctrn_result
  ) dctrn_table

为了绕过CountOutputWalker,我们可以在实例化DoctrineORMAdapter时传递一个标志。所以而不是简单的

代码语言:javascript
复制
$adapter = new DoctrineORMAdapter($qb);

你做了

代码语言:javascript
复制
$adapter = new DoctrineORMAdapter($qb, true, false);

(第三个参数)。这将计数查询转换为效率更高的查询:

代码语言:javascript
复制
SELECT 
  count(DISTINCT m0_.id) AS sclr_0 
FROM 
  messaging_messages m0_

不过,您必须将whiteoctober/Pagerfanta更新为1.0.3。

问题

相关承诺

票数 9
EN

Stack Overflow用户

发布于 2015-02-17 12:40:45

在您的例子中,执行子查询的不是页面幻想。它是来自于查询生成器实例的源。

我通常在实体存储库中有一个函数,它返回一个普通的查询生成器实例,而不是返回结果。您可以编写一个高效的查询生成器。然后,我将查询生成器输入到DoctrineORMAdapter中。

我在整个项目中都使用了这个助手函数:

代码语言:javascript
复制
/**
 * Pass an array, entity or a custom QueryBuilder instance to paginate.
 * Takes an array of parameters as a second argument.
 * Default parameter values:
 *
 * $params = array(
 *     'curPage' => 1,
 *     'perPage' => 15,
 *     'order' => 'DESC'
 * );
 *
 * @param mixed $object
 * @param array $params
 *
 * @return Pagerfanta
 */
public function paginate($object, $params = array())
{
    if (is_array($object)) {
        $adapter = new ArrayAdapter($object);
    } elseif ($this->isEntity($object)) {
        $qb      = $this->em->createQueryBuilder()
            ->select('s')
            ->from($this->getEntityName($object), 's')
            ->orderBy('s.id', isset($params['order']) ? $params['order'] : 'DESC');
        $adapter = new DoctrineORMAdapter($qb);
    } elseif ($object instanceof QueryBuilder) {
        $adapter = new DoctrineORMAdapter($object);
    }
    $pager = new Pagerfanta($adapter);
    $pager->setMaxPerPage(isset($params['perPage']) ? $params['perPage'] : 15);
    $pager->setCurrentPage(isset($params['curPage']) ? $params['curPage'] : 1);

    return $pager;
}

您可以传递一个数组、实体或查询生成器实例,它将返回一个可以使用的适当分页对象。

您可能知道它是如何完成的,但是无论如何,我的实体存储库中有一个函数返回查询构建器实例(对于pagerfanta来说是完美的),另一个函数返回一个用于其他地方的数组:

代码语言:javascript
复制
public function getMessageQueryBuilder($campaignId, $eqCriteriaArray = array(), $neqCriteriaArray = array())
{
    $qb = $this->createQueryBuilder('m');
    $qb->select('m')
        ->leftJoin('m.campaign', 'c')
        ->leftJoin('m.sentBy', 'u')
        ->where($qb->expr()->eq('m.campaign', $campaignId));
    foreach ($eqCriteriaArray as $property => $value) {
        $qb->andWhere($qb->expr()->eq($property, $qb->expr()->literal($value)));
    }
    foreach ($neqCriteriaArray as $property => $value) {
        $qb->andWhere($qb->expr()->neq($property, $qb->expr()->literal($value)));
    }

    return $qb->orderBy('m.id', 'DESC');
}

public function filterMessages($campaignId, $eqCriteriaArray = array(), $neqCriteriaArray = array())
{
    return $this->getMessageQueryBuilder($campaignId, $eqCriteriaArray, $neqCriteriaArray)->getQuery()->getResult();

然后,我将这两者结合起来,得到实际的寻呼机对象:

代码语言:javascript
复制
$singleSmsPager = $this->pagerUtil->paginate(
    $this->em->getRepository('TreasureForgeMessageBundle:Message')
        ->getMessageQueryBuilder(CcToolSender::CAMPAIGN_ID, array(), array('u.username' => 'admin')),
    array(
        'curPage' => $singleSmsPage,
        'perPage' => 10
    )
);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23158951

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档