我正在使用带有 $modelName->find(...) 调用的 CakePHP 来选择相当多的行(可能是数百行)
通常,在 PHP/MySQL 中这当然不是问题,因为您在 while 循环中获取它们。但是,CakePHP 将所有行加载到一个耗尽内存限制的数组中。
有没有办法使用 $modelName->find(...) 构造但返回一个迭代器来按需获取每一行?
谢谢,大卫
如果您的问题是由模型的关系引起的,您可以通过以下方式减少递归:
$模型名->递归=-1;
那么你只会得到当前模型的数据,没有任何关系。
遍历所有记录,您将能够以递归 > 0 再次查询它们的关系
这是可用于一次处理几行表的代码
$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);
不,开箱即用的 Cake PHP(以及一般的 ActiveRecord)不支持迭代这样的结果集。如果您有一个实际需要表的所有记录的用例,那么使用原始 SQL 可能会更好。(或重新考虑您的用例)。
您也可以(并且可能已经考虑过)使用某种偏移量,并多次调用 ->find。如果您采用这种方法,请不要忘记按某个字段对结果集进行排序,以确保获得确定性的结果。当您关闭 ORDER BY 时,数据库似乎只返回已排序的行。我没有亲自尝试过,从多个查询的角度来看它似乎效率低下,但这是值得尝试的。
如果您使用存储过程来生成查询,那么模拟分页行为以一次获取查询段可能是一个好主意。在这种情况下,您将向存储过程发送两个额外参数,用于起始行索引和“page”-size,并返回调整 select 语句以仅检索 row_index + 1 和 row_index + page_size 之间的那些记录。
这都可以放入一个额外的循环层中,所以你得到一个新的段,然后在其中遍历该段中的每一行。
由于您的模型关系,您可能还会获得如此多的数据。对于您的 someModel->find() 调用,还有多少其他模型与 someModel 相关?
我在一个典型的共享托管服务器上使用了至少 1000 行的数组,没有内存问题。
更新:如果您不希望返回相关模型,您可以简单地使用 Containable 行为。因此,如果您尝试查找所有帖子但不想要评论,您可以使用 $this->Post->contain() 语法仅返回帖子记录。Containable 是必须使用 $actAs 参数添加到模型中的行为,$actAs = array('Containable');
我知道您正在寻找从查找条件返回的迭代器,但是如何使用带有可变偏移量的 LIMIT 子句(例如,您希望一次返回的行数)?这可能会带来一些并发问题,但如果您还包括 ORDER BY id 子句,您应该会在返回的行中看到一致的行为。然后,在您的循环中,只需重复发出 find(...) 查询。
这显然不是迭代器的优雅解决方案,但我想重复发出请求以返回更多行的开销将通过一次检索多行(在 Cake 中)与节省的成本相平衡.
最后,如果您真的在寻找性能,那么我认为 CakePHP 可能不是您的理想之选。随着新版本的发布,它的速度正在提高,但我相信它在性能方面仍然明显落后于其他框架。
我想这是不可能的,因为 CakePHP 正在动态构建一个表示您的数据库实体关系的多维数组。这应该在获取所有查询行之后完成,以便 CakePHP 知道所有可能的相关实体。
例子:
为了构建相应的多维数组,需要获取 3 行:
第1条 | -- 评论 1 | -- 评论 2 | -- 评论 3
查询结果(1..n):
文章 | 评论 ----------------- 1 | 1 ----------------- 1 | 2 ----------------- 1 | 3
您可以为查找请求添加限制。我现在没有时间写一个完整的答案。我稍后会更新它。
不,据我所知,当您在 mysql 或普通驱动程序中发出请求时。无论如何,它将返回您选择的所有元素。因此,如果您对内存限制有疑问,它可能在其他地方。您可以为一定数量的行添加限制。如果您的表有多个依赖项,但您不需要加载每个外键,则可以使用“包含”属性仅加载您需要的内容。
你能给我们描述一下你的桌子吗?以及您要选择的内容。
您可以使用递归参数(用于 Model#find 的 API)限制 find 方法调用,也可以即时取消绑定模型关联并减少检索的数据量(即时创建和销毁关联)
已经有一段时间了,但是自从搜索中出现这个问题以来,我想提一下实际上有一种内置的方法可以做到这一点。就像是:
$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 循环中重新执行,因此会带来一些性能损失。
(未测试)
Ruby On rails 可以更好地处理这个问题。默认行为是不包含任何其他表,除非您使用 :include => :table_name 然后它会即时生成连接。
它没有理由不能做到这一点,只是没有。