【问题标题】:CodeIgniter Active Record: Load One Row at a TimeCodeIgniter Active Record:一次加载一行
【发布时间】:2012-11-29 08:26:55
【问题描述】:

文档中描述的普通result() 方法似乎会立即加载所有记录。我的应用程序需要加载大约 30,000 行,一次一个,将它们提交给第三方搜索索引 API。显然,一次将所有内容加载到内存中效果不佳(由于内存过多而导致错误)。

所以我的问题是,如何才能达到传统 MySQLi API 方法的效果,在循环中一次加载一行?

【问题讨论】:

  • 我不认为你可以。您是否尝试过使用LIMITOFFSET 加载多个查询,可能一次加载1000~ 条记录。
  • 从你可以合理传递给搜索 api 的内容中回溯,无论是 10 还是 100 或 1000 等。放置一些东西以防组提交失败,你必须重新提交给搜索api.

标签: php codeigniter mysqli lazy-loading


【解决方案1】:

这是你可以做的。

while ($row = $result->_fetch_object()) {
  $data = array(
    'id'         => $row->id
    'some_value' => $row->some_field_name
  );
  // send row data to whatever api
  $this->send_data_to_api($data);
}

这一次会得到一排。查看 CodeIgniter 源代码,你会发现它们在你执行 result() 方法时会这样做。

【讨论】:

  • 谢谢@elvispt,尽管这取决于内部接口,但它似乎是唯一的选择。
  • 谢谢!以前,当我不得不处理比 PHP 可以在内存中保存的更大的数据集时,我只是使用了常规的旧 mysql 函数。这好多了。
  • 我很惊讶这在 2 年内没有被投票得更高。这是一种节省内存的绝妙方法。如果没有这个,我的一些查询是不可能的:)
【解决方案2】:

对于那些想要在大型结果集上节省内存的人:

CodeIgniter 3.0.0 起, 有一个unbuffered_row函数,

上述所有方法都会将整个结果加载到内存中(预取)。使用 unbuffered_row() 处理大型结果集。

 

此方法返回单个结果行,而不像 row() 那样将整个结果预取到内存中。如果您的查询有多于一行,它会返回当前行并将内部数据指针向前移动。

$query = $this->db->query("YOUR QUERY");

while ($row = $query->unbuffered_row())
{
    echo $row->title;
    echo $row->name;
    echo $row->body;
}

您可以选择传递“对象”(默认)或“数组”以指定返回值的类型:

$query->unbuffered_row();               // object
$query->unbuffered_row('object');       // object
$query->unbuffered_row('array');        // associative array

官方文档:https://www.codeigniter.com/userguide3/database/results.html#id2

【讨论】:

  • 嗨@Tiger,我知道这是一个旧线程,但我可以问一个问题吗?我应该在我的控制器上实现 unbuffered_rows 吗?如果没有,那么它可能应该在模型上。但如果是这种情况,我如何一次将数据传递给我的控制器?请多多包涵,我还在学习 CodeIgniter。我很好奇如何加快我的数据检索速度。谢谢……
  • @DanAngeloAlcanar 3 年后我也有同样的问题
【解决方案3】:

嗯,有一个 row() 方法,它只返回一行作为对象,或者 row_array() 方法,它做同样的事情但返回一个数组(当然)。

所以你可以做类似的事情

$sql = "SELECT * FROM yourtable";
$resultSet = $this->db->query($sql);
$total = $resultSet->num_rows();

for($i=0;$i<$total;$i++) {
  $row = $resultSet->row_array($i);
}

这会从整个结果集中循环获取每一行。
这与获取所有信息并循环访问我相信的 $this-&gt;db-&gt;query($sql)-&gt;result() 方法调用大致相同。

如果您想要一次一行,您可以进行 30.000 次调用,或者选择所有结果并一次获取它们,或者您获取所有结果并遍历数组。我现在看不到任何出路。

【讨论】:

  • 但是整个集合在任何给定时间都不能在内存中。我想选择一行,将其推送到 API,然后处理该数据,然后重复。
【解决方案4】:

嗯,问题是result() 泄露了查询的全部回复。 row() 只需获取第一个案例并转储其余案例。但是,无论您使用哪个函数,查询仍然可以获取 30 000 行。

适合您事业的设计是:

$offset = (int)@$_GET['offset'];

$query = $this-db->query("SELECT * FROM table LIMIT ?, 1", array($offset));
$row = $query->row();

if ($row) {

    /* Run api with values */

    redirect(current_url().'?offset'.($offset + 1));

}

这将占用一行,将其发送到 api,更新页面并使用下一行。它还可以防止页面超时。但是,30 000 条记录和刷新很可能需要一段时间,因此您可能需要将 LIMIT ?, 1 调整为比 1 更高的数字,然后在每次页面加载时使用 result()foreach() 多个 api。

【讨论】:

  • 不幸的是,HTTP 重定向在这种情况下不起作用,因为它是通过命令行调用的。好像不是一个超实用的解决方案……我想我可能要直接使用数据库适配器?
  • 你可以每分钟调用一次,并通过一个额外的 mysql 列检查 em,这样它就会为你 WHERE api = 'FALSE'
猜你喜欢
  • 2015-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-24
  • 1970-01-01
  • 2013-01-11
  • 2014-11-12
相关资源
最近更新 更多