在教义QueryBuilder中计算行数


197

我正在使用Doctrine的QueryBuilder来构建查询,并且我想从查询中获取结果的总数。

$repository = $em->getRepository('FooBundle:Foo');

$qb = $repository->createQueryBuilder('n')
        ->where('n.bar = :bar')
        ->setParameter('bar', $bar);

$query = $qb->getQuery();

//this doesn't work
$totalrows = $query->getResult()->count();

我只想对此查询进行计数以获取总行,但不返回实际结果。(在此计数查询之后,我将使用maxResults进一步修改查询以进行分页。)


1
您只想返回结果数?您的代码不是很清楚。为什么getQuery()不起作用?
杰里2012年

要使用doctrine2建立分页,请查看以下扩展:github.com/beberlei/DoctrineExtensions
Stefan

Answers:


474

就像是:

$qb = $entityManager->createQueryBuilder();
$qb->select('count(account.id)');
$qb->from('ZaysoCoreBundle:Account','account');

$count = $qb->getQuery()->getSingleScalarResult();

有些人认为表达式比仅使用直接DQL更好。甚至有人甚至编辑了一个四年的答案。我回滚了他的编辑。去搞清楚。


他没有要求没有谓词的计数(bar = $bar);)
Jovan Perovic

4
他接受了您的回答,所以我想一切都很好。我的印象是,他只想要一个数,而没有实际检索示例中显示的行的开销。当然,没有理由不能添加条件。
塞拉德(Cerad)2012年

50
+1用于使用getSingleScalarResult()。使用count()on $query->getResult()实际上是使查询返回结果(这是他想要的)。我认为应该接受这个答案
jere 2012年

18
最便携的方法是做$qb->select($qb->expr()->count('account.id'))
webbiedave '16

1
有人可以解释为什么我必须使用select('count(account.id)')代替select('count(account)')吗?
Stepan Yudin

51

这是格式化查询的另一种方法:

return $repository->createQueryBuilder('u')
            ->select('count(u.id)')
            ->getQuery()
            ->getSingleScalarResult();

使用流利的界面是另一种方法,在您打算编写静态查询时非常有用。如果需要切换条件,例如自己执行每个方法的条件,那么它也是有优势的。
barbieswimcrew

3
您可以编写此内容return ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->count('u.id'))->getQuery()->getSingleScalarResult();
-Barh,

25

最好将使用数据库的所有逻辑转移到存储库。

所以在控制器中你写

/* you can also inject "FooRepository $repository" using autowire */
$repository = $this->getDoctrine()->getRepository(Foo::class);
$count = $repository->count();

而在 Repository/FooRepository.php

public function count()
{
    $qb = $repository->createQueryBuilder('t');
    return $qb
        ->select('count(t.id)')
        ->getQuery()
        ->getSingleScalarResult();
}

最好移动$qb = ...到单独的行,以防您要编写复杂的表达式,例如

public function count()
{
    $qb = $repository->createQueryBuilder('t');
    return $qb
        ->select('count(t.id)')
        ->where($qb->expr()->isNotNull('t.fieldName'))
        ->andWhere($qb->expr()->orX(
            $qb->expr()->in('t.fieldName2', 0),
            $qb->expr()->isNull('t.fieldName2')
        ))
        ->getQuery()
        ->getSingleScalarResult();
}

另外考虑一下缓存查询结果-http: //symfony.com/doc/current/reference/configuration/doctrine.html#caching-drivers

public function count()
{
    $qb = $repository->createQueryBuilder('t');
    return $qb
        ->select('count(t.id)')
        ->getQuery()
        ->useQueryCache(true)
        ->useResultCache(true, 3600)
        ->getSingleScalarResult();
}

在某些简单的情况下,使用EXTRA_LAZY实体关系很好
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html


17

如果你需要计算更复杂的查询,用groupByhaving等等......你可以借用Doctrine\ORM\Tools\Pagination\Paginator

$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query);
$totalRows = count($paginator);

8
有用,但请注意:此解决方案适用于单个实体上的查询-具有复杂的select语句,它将拒绝使用。
Paolo Stefan

此解决方案会产生其他查询,例如SELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_table实际上并没有什么特别的问题,而是众所周知的COUNT(*)解决方案
Vladyslav Kolesov 2016年

$ paginator-> getTotalItemCount()也将是一个解决方案
悄悄话'17

11

由于Doctrine 2.6可以count()直接使用方法EntityRepository。有关详细信息,请参见链接。

https://github.com/doctrine/doctrine2/blob/77e3e5c96c1beec7b28443c5b59145eeadbc0baf/lib/Doctrine/ORM/EntityRepository.php#L161


是的,它看起来是一个不错的解决方案,并且适用于更简单的情况(您可以通过过滤计数的条件),但是我没有设法使其用于具有关联的条件(按关联过滤)。在此处查看相关文章:github.com/doctrine/orm/issues/6290
Wilt

6

使用分组,联合和填充的示例。

问题:

 $qb = $em->createQueryBuilder()
     ->select('m.id', 'rm.id')
     ->from('Model', 'm')
     ->join('m.relatedModels', 'rm')
     ->groupBy('m.id');

为此,可能的解决方案是使用自定义水化器和名为“ CUSTOM OUTPUT WALKER HINT”的怪异事物:

class CountHydrator extends AbstractHydrator
{
    const NAME = 'count_hydrator';
    const FIELD = 'count';

    /**
     * {@inheritDoc}
     */
    protected function hydrateAllData()
    {
        return (int)$this->_stmt->fetchColumn(0);
    }
}
class CountSqlWalker extends SqlWalker
{
    /**
     * {@inheritDoc}
     */
    public function walkSelectStatement(AST\SelectStatement $AST)
    {
        return sprintf("SELECT COUNT(*) AS %s FROM (%s) AS t", CountHydrator::FIELD, parent::walkSelectStatement($AST));
    }
}

$doctrineConfig->addCustomHydrationMode(CountHydrator::NAME, CountHydrator::class);
// $qb from example above
$countQuery = clone $qb->getQuery();
// Doctrine bug ? Doesn't make a deep copy... (as of "doctrine/orm": "2.4.6")
$countQuery->setParameters($this->getQuery()->getParameters());
// set custom 'hint' stuff
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountSqlWalker::class);

$count = $countQuery->getResult(CountHydrator::NAME);

7
我宁愿只编写本机查询,也不愿处理该Rube Goldberg代码。
keyboardSmasher

这是一个很好的例子,说明Symfony多么卑鄙:像简单的日常SQL计数这样的简单事情,需要用完全复杂的自写东西来解决……哇,我的意思是,哇!仍然感谢Sergey的回答!
Sliq

4

对于仅使用Doctrine DBAL而不使用Doctrine ORM的人,他们将无法访问该getQuery()方法,因为该方法不存在。他们需要执行以下操作。

$qb = new QueryBuilder($conn);
$count = $qb->select("count(id)")->from($tableName)->execute()->fetchColumn(0);

4

要在一定数量的项目之后对项目进行计数(偏移量),在这种情况下,不能应用$ qb-> setFirstResults(),因为它不是作为查询条件,而是作为所选结果范围内查询结果的偏移量(即setFirstResult根本不能与COUNT一起使用)。因此,要计算剩余的项目,我只需执行以下操作:

   //in repository class:
   $count = $qb->select('count(p.id)')
      ->from('Products', 'p')
      ->getQuery()
      ->getSingleScalarResult();

    return $count;

    //in controller class:
    $count = $this->em->getRepository('RepositoryBundle')->...

    return $count-$offset;

有人知道更干净的方法吗?


0

在存储库中添加以下方法应该可以使您$repo->getCourseCount()从Controller 调用。

/**
 * @return array
 */
public function getCourseCount()
{
    $qb = $this->getEntityManager()->createQueryBuilder();

    $qb
        ->select('count(course.id)')
        ->from('CRMPicco\Component\Course\Model\Course', 'course')
    ;

    $query = $qb->getQuery();

    return $query->getSingleScalarResult();
}

0

您还可以通过使用计数功能来获取数据数量。

$query = $this->dm->createQueryBuilder('AppBundle:Items')
                    ->field('isDeleted')->equals(false)
                    ->getQuery()->count();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.