【问题标题】:Retrieve all rows from table in doctrine从学说中的表中检索所有行
【发布时间】:2023-03-29 16:51:02
【问题描述】:

我有 100 000+ 行的表,我想在学说中选择所有这些,并对每一行执行一些操作,在 symfony2 中我尝试使用以下查询:

    $query = $this->getDefaultEntityManager()
        ->getRepository('AppBundle:Contractor')
        ->createQueryBuilder('c')
        ->getQuery()->iterate();

    foreach ($query as $contractor) {
        // doing something
    }

但后来我得到了内存泄漏,因为我认为它把所有数据都写在了内存中。

当我这样做时,我在那个库中的 ADOdb 方面有更多经验:

$result = $ADOdbObject->Execute('SELECT * FROM contractors');
   while ($arrRow = $result->fetchRow()) {
        // do some action
   }

我没有发现任何内存泄漏。

那么如何从表中选择所有数据,并且在 symfony2 中不会出现内存泄漏?

问题编辑

当我尝试删除 foreach 并进行迭代时,我也会遇到内存泄漏:

$query = $this->getDefaultEntityManager()
            ->getRepository('AppBundle:Contractor')
            ->createQueryBuilder('c')
            ->getQuery()->iterate();

【问题讨论】:

    标签: php symfony doctrine-orm doctrine adodb-php


    【解决方案1】:

    正常的做法是使用iterate()

    $q = $this->getDefaultEntityManager()->createQuery('select u from AppBundle:Contractor c');
    $iterableResult = $q->iterate();
    foreach ($iterableResult as $row) {
        // do something
    }
    

    但是,正如教义文档所说,这仍然会导致错误。

    数据库客户端/连接可能会完全缓冲结果,从而分配 PHP 进程不可见的额外内存。对于大型集合,这可能很容易无缘无故地终止进程。

    最简单的方法是简单地创建带有偏移量和限制的较小查询。

    //get the count of the whole query first
    $qb = $this->getDefaultEntityManager();
    $qb->select('COUNT(u)')->from('AppBundle:Contractor', 'c');
    $count = $qb->getQuery()->getSingleScalarResult();
    
    //lets say we go in steps of 1000 to have no memory leak
    $limit = 1000;
    $offset = 0;
    
    //loop every 1000 > create a query > loop the result > repeat
    while ($offset < $count){
        $qb->select('u')
            ->from('AppBundle:Contractor', 'c')
            ->setMaxResults($limit)
            ->setFirstResult($offset);
        $result = $qb->getQuery()->getResult();
        foreach ($result as $contractor) {
            // do something
        }
        $offset += $limit;
    }
    

    对于如此庞大的数据集,这很可能会超过最大执行时间,默认情况下为 30 秒。因此,请确保在您的 php.ini 中手动更改 set_time_limit。如果您只想使用已知模式更新所有数据集,您应该考虑编写一个大型更新查询,而不是在 PHP 中循环和编辑结果。

    【讨论】:

    • 嗨@Hosch 好建议!您可以避免使用调用set_time_limit(0) 覆盖值的单个方法的最大执行时间,如doc 中所述
    • 使用 iterate 我仍然会出现内存泄漏,但第二个报价还可以 :)
    • 是的,我曾经在 cronjob 上遇到过同样的问题,并用这种循环解决了它。
    【解决方案2】:

    尝试使用这种方法:

    foreach ($query as $contractor) {
        // doing something
    
        $this->getDefaultEntityManager()->detach($contractor);
        $this->getDefaultEntityManager()->clear($contractor);
        unset($contractor); // tell to the gc the object is not in use anymore
    
    }
    

    希望有帮助

    【讨论】:

      【解决方案3】:

      如果你真的需要获取所有记录,我建议你直接使用database_connection。查看它的界面并选择不会将所有数据加载到内存中(并且不会将记录映射到您的实体)的方法。

      你可以使用这样的东西(假设这段代码在控制器中):

      $db = $this->get('database_connection');
      $query = 'select * from <your_table>';
      $sth = $db->prepare($query);
      $sth->execute();
      while($row = $sth->fetch()) {
          // some stuff
      }
      

      可能这不是您所需要的,因为您可能希望在处理完所有集合后拥有对象。但也许你不需要这些对象。还是想想吧。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-19
        • 2014-06-28
        • 1970-01-01
        • 2022-10-14
        相关资源
        最近更新 更多