在 Doctrine QueryBuilder 中计算行数

Posted

技术标签:

【中文标题】在 Doctrine QueryBuilder 中计算行数【英文标题】:Count Rows in Doctrine QueryBuilder 【发布时间】:2021-12-13 23:10:32 【问题描述】:

我正在使用 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 进一步修改查询以进行分页。)

【问题讨论】:

你只是想返回结果的数量?你的代码不是很清楚。为什么 getQuery() 不起作用? 要使用教义2构建分页,请查看此扩展:github.com/beberlei/DoctrineExtensions @Stefan 现在是 ORM 的一部分。 docs.doctrine-project.org/en/latest/tutorials/pagination.html 【参考方案1】:

类似:

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

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

有些人觉得表达式比直接使用 DQL 更好。甚至有人编辑了一个四年前的答案。我回滚了他的编辑。去图吧。

【讨论】:

他没有要求没有谓词的计数 (bar = $bar) ;) 他接受了你的回答,所以我想一切都很好。我的印象是他只想要一个计数,而不需要实际检索我的示例显示的行的开销。 where 条件当然没有不能添加的原因。 +1 用于使用 getSingleScalarResult()。在$query->getResult() 上使用count() 实际上是让查询返回结果(这是他想要的)。我认为这应该被接受的答案 最便携的方式是$qb->select($qb->expr()->count('account.id')) 谁能解释为什么我必须使用select('count(account.id)')而不是select('count(account)')【参考方案2】:

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

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

【讨论】:

使用 fluent 界面是一种不同的方法,如果您打算编写静态查询,它非常有用。如果需要切换条件,例如单独执行每个方法也有其优势。 你可以写这个return ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->count('u.id'))->getQuery()->getSingleScalarResult();【参考方案3】:

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

所以在控制器中你写

/* 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

【讨论】:

【参考方案4】:

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

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

【讨论】:

有用,但请注意:此解决方案适用于单个实体的查询 - 使用复杂的选择语句,它只会拒绝工作。 这个解决方案会产生额外的查询,比如SELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_table,这实际上并没有什么特别之处,而是众所周知的 COUNT(*) 解决方案 $paginator->getTotalItemCount() 也是一个解决方案【参考方案5】:

由于Doctrine 2.6,可以直接从EntityRepository 使用count() 方法。详情见链接。

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

【讨论】:

是的,它看起来是一个很好的解决方案,适用于更简单的情况(您可以传递过滤计数的标准),但我没有设法让它适用于具有关联的标准(过滤由协会)。在此处查看相关帖子:github.com/doctrine/orm/issues/6290【参考方案6】:

使用分组、联合和东西的示例。

问题:

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

为此,可能的解决方案是使用自定义水合器和这个奇怪的东西 称为“自定义输出沃克提示”:

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

【讨论】:

我宁愿只写一个本地查询也不愿处理 Rube Goldberg 代码。 这是一个很好的例子,说明 Symfony 是多么糟糕:像基本日常 SQL 计数这样简单的事情需要用完全复杂的自行编写的东西来解决……哇,我的意思是,哇!仍然感谢 Sergey 的回答!【参考方案7】:

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

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

【讨论】:

【参考方案8】:

要在一定数量的项目(偏移量)之后计算项目,在这种情况下不能应用 $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;

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

【讨论】:

【参考方案9】:

您还可以使用count函数获取数据的数量。

$query = $this->dm->createQueryBuilder('AppBundle:Items')
                    ->field('isDeleted')->equals(false)
                    ->getQuery()->count();

【讨论】:

【参考方案10】:

将以下方法添加到您的存储库应该允许您从控制器调用$repo->getCourseCount()

/**
 * @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();

【讨论】:

以上是关于在 Doctrine QueryBuilder 中计算行数的主要内容,如果未能解决你的问题,请参考以下文章

在 Doctrine QueryBuilder 中计算行数

doctrine queryBuilder

Doctrine queryBuilder:addOrderBy() 方法中的 SQL 注入风险?

Doctrine preLoad Listener 在执行前操纵 QueryBuilder

symfony doctrine QueryBuilder到数组结果

Doctrine Querybuilder ORDER BY 子句不在 SELECT 列表中