【问题标题】:Can I stop CakePHP fetching all rows for a query?我可以停止 CakePHP 获取查询的所有行吗?
【发布时间】:2018-04-24 19:11:23
【问题描述】:

我正在使用带有 $modelName->find(...) 的 CakePHP 调用选择相当多的行(可能是数百行)

通常,在 PHP/MySQL 中这当然不是问题,因为您在 while 循环中获取它们。 但是,CakePHP 将所有行加载到耗尽内存的数组中 限制。

有没有办法使用 $modelName->find(...) 构造但返回一个 迭代器按需获取每一行?

谢谢, 大卫

【问题讨论】:

    标签: php mysql database cakephp


    【解决方案1】:

    如果你的问题是由模型的关系引起的,你可以这样减少递归:

    $modelname->recursive = -1;

    那么你只会得到当前模型的数据,没有任何关系。

    遍历所有的记录,你就可以通过递归 > 0 再次得到它们的关系一个一个的查询

    【讨论】:

      【解决方案2】:

      您可以使用以下代码一次处理几行表格

          $limit = 10;
          $loop_no = 0;
          do {
              $handles = $this->SocialChannelHandle->find('all', array(
                  'fields' => array('brand_id', 'handle'),
                  'conditions' => array('social_channel_id' => $facebook['SocialChannel']['id']),
                  'limit'  => $limit,
                  'offset' => $limit * $loop_no,
                  'order'  => 'id asc',
                  'recursive' => -1)
              );
              $loop_no++;
          } while (count($handles) == $limit);
      

      【讨论】:

        【解决方案3】:

        不,开箱即用的 Cake PHP(以及一般的 ActiveRecord)不支持迭代这样的结果集。如果您有一个实际需要所有表记录的用例,那么最好使用原始 SQL。 (或重新考虑您的用例)。

        您也可以(并且可能已经考虑过)使用某种偏移量,并多次调用 ->find。如果您采用这种方法,请不要忘记按某个字段对结果集进行排序,以确保获得确定性的结果。当您关闭 ORDER BY 时,数据库似乎只返回已排序的行。我没有亲自尝试过,从多个查询的角度来看它似乎效率低下,但这是值得一试的。

        【讨论】:

        • 实际上,我现在正在使用 PDO 和缓冲查询来实现这些东西。我从实现可迭代的 find 函数返回一个对象,并具有该语句的句柄。 stackoverflow.com/questions/531536/…
        • 而且人们说蛋糕很慢...呵呵!
        • 使用内置分页! $this->Model->paginate()
        【解决方案4】:

        如果您使用存储过程来生成查询,最好模拟分页行为一次获取​​查询的片段。在这种情况下,您将向存储过程发送两个额外参数,用于起始行索引和“页面”大小,并返回调整 select 语句以仅检索 row_index + 1 和 row_index + page_size 之间的那些记录。

        这都可以放入一个额外的循环层中,这样你就会得到一个新的段,然后在其中遍历该段中的每一行。

        【讨论】:

        • 谢谢,我想这是一个解决方案。唯一的问题是内存和查询之间存在权衡。所以,如果我有 1000 个查询并且我有 100 个内存,那么我必须进行 10 个查询。但我仍然必须一次存储 100 行。在 10,000 或 100,000 时变得更糟……真的很不幸。戴夫
        • 这取决于你对另一边的数据做了什么。如果您正在显示它,那么您可以使用 AJAX 来根据传入的每个新块继续填充,这将节省最初加载页面的时间。程序员的祸根,你必须用时间换取空间,反之亦然。
        • 我正在遍历所有行(它们是其他表中行的元数据……它们是其他行的子行……)并进行一些统计以生成汇总数组。我只需要“查看”每一行一次就可以了,即使需要一点时间,但加载是一个问题。似乎抵消了。
        【解决方案5】:

        我知道您正在寻找从查找条件返回的迭代器,但是如何使用带有可变偏移量的 LIMIT 子句(例如,您希望一次返回的行数)?这可能会带来一些并发问题,但如果您还包括 ORDER BY id 子句,您应该会在返回的行中看到一致的行为。然后,在您的循环中,重复发出 find(...) 查询。

        这显然不是一个优雅的迭代器解决方案,但我想重复发出请求以返回更多行的开销将通过一次检索多行来平衡节省(在蛋糕中)。

        最后,如果您真的在寻找性能,那么我认为 CakePHP 可能不是您的理想之选。随着新版本的发布,它的速度正在提高,但我相信它在性能方面仍然大大落后于其他框架。

        【讨论】:

        • 我想使用 offset 和 limit 是最好的方法。我希望不必求助于它,假设有一种方法可以手动访问 CalePHP 的获取方法。这很不幸,因为我正在做的事情应该非常有效,但是开销太大了。 Arg...戴夫
        • Overhead 是 Cake 的游戏名称!
        【解决方案6】:

        我猜这是不可能的,因为 CakePHP 正在动态构建一个多维数组来表示您的数据库实体关系。这应该在获取所有查询行之后完成,以便 CakePHP 知道所有可能的相关实体。

        例子:

        需要获取3行才能构建对应的多维数组:

        第1条 | -- 评论 1 | -- 评论 2 | -- 评论 3

        查询结果(1..n):

        文章 |评论 ----------------- 1 | 1 ----------------- 1 | 2 ----------------- 1 | 3

        【讨论】:

        • 不确定我是否理解。通常即使具有复杂的层次结构,您也可以使用恒定数量的查询。尤其是在 CakePHP 中它加载到内存中的方式。所以,如果你有两篇文章,每篇有五十个 cmets 你可以先获取文章,然后 SELECT * cmets WHERE article_id IN(article_ids)
        • 这是无效的,也是不好的做法。 CakePHP 使用 eager loading 来防止可怕的 1+N 问题,在该问题中,获取 100 个帖子,每个帖子需要显示其作者会触发 101 个数据库查询。通过使用急切加载,101 个查询可以减少到 2 个。
        【解决方案7】:

        由于您的模型关系,您可能还会获得如此多的数据。对于您的 someModel->find() 调用,还有多少其他模型与 someModel 相关?

        我在典型的共享托管服务器上使用了至少 1000 行的数组,没有内存问题。

        更新:如果您不希望返回相关模型,您可以简单地使用 Containable 行为。因此,如果您尝试查找所有帖子但不想要评论,您可以使用 $this->Post->contain() 语法仅返回帖子记录。 Containable 是必须使用 $actAs 参数添加到模型中的行为,$actAs = array('Containable');

        【讨论】:

          【解决方案8】:

          您可以为查找请求添加限制。我现在没有时间写一个完整的答案。我稍后会更新它。

          不,据我所知,当您在 mysql 或普通驱动程序中发出请求时。无论如何,它将返回您选择的所有元素。因此,如果您对内存限制有疑问,它可能在其他地方。您可以为一定数量的行添加限制。如果您的表有多个依赖项,但您不需要加载每个外键,则可以使用“包含”属性仅加载您需要的内容。

          你能给我们描述一下你的桌子吗?以及您要选择的内容。

          【讨论】:

            【解决方案9】:

            您可以使用递归参数 (API for Model#find) 限制 find 方法调用,也可以即时取消绑定模型关联并减少检索的数据量 (Creating and Destroying Associations on the Fly)

            【讨论】:

              【解决方案10】:

              已经有一段时间了,但是自从搜索中出现这个问题以来,我想提一下实际上有一种内置的方法可以做到这一点。 比如:

              $page = 1;
              $limit = 100;
              while ($posts = $this->Post->find('all', array(
                 'conditions' => ...,
                 'page' => $page,
                 'limit' => $limit,
                 ...
              ))) {
                  foreach ($posts as $post) {
                      ...deal with one row...
                  }
                  $page++;
              }
              

              将通过数据集进行分页,尽管会因在每个 while 循环中重新执行查询而造成一些性能损失。

              (未测试)

              【讨论】:

                【解决方案11】:

                Ruby On rails 可以更好地处理这个问题。默认行为是不包含任何其他表,除非您使用 :include => :table_name 然后它会即时生成连接..

                没有理由不能这样做,只是没有。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-11-15
                  • 2011-11-01
                  • 2018-05-15
                  • 2011-01-06
                  • 1970-01-01
                  • 2010-11-21
                  • 1970-01-01
                  相关资源
                  最近更新 更多