我正在开发一个应用程序,用户应该在其中看到一个带有图表的列表。图表的数据应该从数据库(目前约有785行)中提取,然后进行排序以形成有效的JSON字符串。对不对,我试着像这个一样做
while($row = $res->fetch_assoc()) {
if(count($appData) == 0 ){
$appData[] = array(
"name" => $row["name"],
"date" => array($row["date"]),
"android" => array($row["android_count"]),
"ios" => array($row["apple_count"])
);
}else {
for($i = 0; $i < count($appData); $i++) {
if($appData[$i]["name"] == $row["name"]){
$appData[$i]["date"][] = $row["date"];
$appData[$i]["android"][] = $row["android_count"];
$appData[$i]["ios"][] = $row["apple_count"];
}else {
$appData[] = array(
"name" => $row["name"],
"date" => array($row["date"]),
"android" => array($row["android_count"]),
"ios" => array($row["apple_count"])
);
}
}
}
}
echo json_encode($appData);
当我尝试运行代码时,它会给出一个"致命错误:允许的内存大小为536870912字节已耗尽(试图分配71字节)"的错误。我试着增加允许的最大内存,只是想看看会发生什么,但我得到了同样的结果。
有什么办法可以避免做这么多循环吗?或者我应该用一种完全不同的方式来处理这个问题,如果是,那是哪一种?
最终结果应该看起来像这个
[{"name":"Some name", "date":["2016-05-09", "2016-05-10", "2016-05-11"], "android":["3", "1", "8"], "ios":["4", "7", "5"]},...]
我们将不胜感激!
内存问题在"for"循环中。它可以在每个循环中向$appData添加一堆项,而不是"如果没有匹配的名称,则只添加一个项"。例如,如果$appData中已经有100个项,并且$row['name']与$appData的最后一个项匹配,那么在更新$appData内的最后一项之前,将有99个项添加到$appData。我敢打赌,当前的代码正在生成一个包含785个项目的$appData。
要解决内存问题,请将"for"循环更改为以下内容:
$matchFound = false;
for($i = 0; $i < count($appData); $i++) {
if($appData[$i]["name"] == $row["name"]){
$appData[$i]["date"][] = $row["date"];
$appData[$i]["android"][] = $row["android_count"];
$appData[$i]["ios"][] = $row["apple_count"];
$matchFound = true;
break;
}
}
if (!$matchFound) {
$appData[] = array(
"name" => $row["name"],
"date" => array($row["date"]),
"android" => array($row["android_count"]),
"ios" => array($row["apple_count"])
);
}
在效率方面,使用maximkou建议的关联数组将是一个很大的加速。
问题不在于循环的数量,而在于$appData
数组的大小和php配置的memory_limit值。
如果无法缩小传递的数据的大小,则必须增加memory_limit值。但是在增加这个值时要小心,因为它是服务器将执行的每个正在运行的php脚本的值。我建议在每个循环中对输出缓冲区进行分页或发送。
如果您需要代码示例,只需索取即可。
分页意味着您的javascript页面将调用PHP脚本X次,每次检索N行,直到PHP脚本不再允许为止。因此,您必须返回一个数组,例如:
return array(
'nextPage' => 2, // More data available on this page
'data' => $json
);
// Or
return array(
'nextPage' => null, // No more data available
'data' => $json
);
或者发送到每个循环上的输出缓冲区并释放内存:
$first = true;
echo '[';
while($row = $res->fetch_assoc()) {
if(!$first) {
echo ',';
} else {
$first = false;
}
// some logic
$row_data = array(...);
echo json_encode($row_data);
}
echo ']';
这样就不会在php的变量中堆叠所有数据。
通过$row['name']
对数组进行索引。这种强大的功能简化了您的代码。php中的数组分配了许多内存,因此按行对嵌套数据进行编码。如果您知道结果数组的大小,也可以尝试使用SplFixedArray。
试试这个:
while($row = $res->fetch_assoc()) {
$appData[ $row["name"] ] = json_encode(array(
"name" => $row["name"],
"date" => array($row["date"]),
"android" => array($row["android_count"]),
"ios" => array($row["apple_count"])
));
}
echo "[".implode(',', $appData)."]";
这应该会创建完全相同的结果(tho尚未测试),并使用映射数组和array_key_exists()
来避免额外的循环。这是在一个循环中完成的。
$nameMap = array(); // hold name and keys
while($row = $res->fetch_assoc()){
$key = array_key_exists($row['name'], $nameMap) ? $nameMap[$row['name']] : count($appData);
$nameMap[$row['name']] = $key;
if(empty($appData[$key]))
$appData[$key] = array("name"=>$row['name'], "date"=>array(), "android"=>array(), "ios"=>array());
$appData[$key]['date'][] = $row['date'];
$appData[$key]['android'][] = $row['android'];
$appData[$key]['ios'][] = $row['ios'];
}