我正在使用Doctrine2 and Zf2
,现在当我需要获取count
行时,我有以下两种方法来获取它。但我担心的是哪种方式会更优化、更快,因为将来行将超过 50k。任何建议或任何其他方法来获取计数??是否有任何函数可以获取计数,可以与findBy
???一起使用
或者我应该使用普通的 Zf2 数据库库来获取计数。我刚刚发现,当数据巨大时,ORM不是获取结果的首选。请任何帮助将不胜感激
$members = $this->getEntityManager()->getRepository('UserEntityMembers')->findBy(array('id' => $id, 'status' => '1'));
$membersCnt = sizeof($members);
或
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('count(p)')
->from('UserEntityMembers', 'p')
->where('p.id = '.$id)
->andWhere('p.status = 1');
$membersCnt = $qb->getQuery()->getSingleScalarResult();
比较
1)您的EntityRepository::findBy()
方法将做到这一点:
- 在数据库中查询与您的条件匹配的行。数据库将返回完整的行。
- 然后将数据库结果转换(水合)为完整的 PHP 对象(实体)。
2)您的EntityManager::createQueryBuilder()
方法将做到这一点:
- 在数据库中查询与您的条件匹配的行数。数据库将返回一个简单的数字(实际上是一个表示数字的字符串)。
- 然后将数据库结果从字符串转换为 PHP 整数。
您可以有把握地得出结论,选项 2 比选项 1 有效得多:
- 数据库可以优化查询以进行计数,这可能会使查询更快(花费更少的时间)。
- 从数据库返回的数据要少得多。
- 没有实体是水合的(只有一个简单的字符串到整数转换)。
总而言之,将使用更少的处理能力和更少的内存。
安全评论
切勿将值连接到查询中! 当这些值是(派生自)用户输入时,这可能会使您容易受到 SQL 注入攻击。
此外,Doctrine2 不能使用预准备语句/参数绑定,当经常使用相同的查询(使用或不使用不同的参数)时,这可能会导致一些性能损失。
换句话说,替换它:
->where('p.id = '.$id)
->andWhere('p.status = 1')
有了这个:
->where('p.id = :id')
->andWhere('p.status = :status')
->setParameters(array('id' => $id, 'status' => 1))
或:
->where($qb->expr()->andX(
$qb->expr()->eq('p.id', ':id'),
$qb->expr()->eq('p.status', ':status')
)
->setParameters(array('id' => $id, 'status' => 1))
此外
对于此特定查询,无需使用QueryBuilder
,您可以使用直接 DQL 代替:
$dql = 'SELECT COUNT(p) FROM UserEntityMembers p WHERE p.id = :id AND p.status = :status';
$q = $this->getEntityManager()->createQuery($dql);
$q->setParameters(array('id' => $id, 'status' => 1));
$membersCnt = $q->getSingleScalarResult();
你应该完全转到 dql 版本的计数。
使用第一种方法,您将水化(从数据库结果集转换为对象)每行作为单个对象,并将它们放在一个数组中,然后计算该数组中的项目数量。如果唯一目标是知道该结果集中的元素数,那将完全浪费内存和周期。
使用第二种方法,dql 将被优雅地转换为 SELECT COUNT(*) 等等 普通的SQL句子,并将直接从数据库中检索计数。
关于ORM的评论不是首选何时检索数据是巨大的,在大批量过程中,您应该分页查询以同时检索数据以避免内存覆盖,但在这种情况下,您只检索一个数字,总数,因此此规则不适用。
查询生成器太慢了。
-
使用 DQL 可以更快地选择 。
$query = $this->getEntityManager()->createQuery("SELECT count(m) FROM UserEntityMembers m WHERE m.status = 1 AND m.id = :id "); $query->setParameter(':id', $id);
您需要设置参数来防止SQL注入。
-
存储过程是最快的,但它取决于您的数据库。
- 使实体的所有关系变得懒惰。