PDO内存耗尽

时间:2015-06-09 05:30:24

标签: php mysql pdo mariadb

这是常见问题,但我没有选择这样编码只是为了在Excel文件中获取适当的标题和正文

这里是如何开始的

当请求打印时,我首先开始查询以获取数据库中的标题

SELECT instruments.in_id, instrument_parameters.ip_id,
CASE WHEN gv_x_ipid = -1 THEN 'datetime' ELSE '' END xlabel,
CASE WHEN ip_label LIKE '%Reservoir%' THEN 0 ELSE in_order END legendIndex,
CASE WHEN in_name = 'General' THEN ip_label ELSE in_name END ylabel            
FROM graph_plot
LEFT JOIN attributes gptype ON gp_type = gptype.at_id
LEFT JOIN graph_value ON gp_id = gv_gpid
LEFT JOIN instrument_parameters ON gv_y_ipid = ip_id
LEFT JOIN attributes pmunit ON ip_unit = pmunit.at_id
LEFT JOIN instrument_reading yvalue ON gv_y_ipid = iv_ipid
LEFT JOIN instruments ON iv_inid = in_id
WHERE gp_diid = :di_id AND 
      gp_type = :rpt_type AND 
      iv_status = 'Y' AND
      iv_inid in (".implode(",", $coll->inid).") AND
      gv_y_ipid in (".implode(",", $coll->ipid).")
GROUP BY ylabel
ORDER BY legendIndex

这将产生一些标题,我将使它像这样

DATE | Instrument1 | Instrument2 | Instrument3

基于上面的查询,Instrument?将是动态的。我将它存储在新变量中。但保存数据库结果的原始变量保持不变。

稍后,使用相同的参数:di_id:rpt_type,还有另外一个参数,startDtendDt来进行另一个查询,只是为了返回一个可用的长列表数据库中的日期。这基于startDtendDt

$sql2 = "SELECT iv_reading FROM instrument_reading WHERE iv_inid = :inid AND iv_ipid = :ipid AND iv_date = :dt AND iv_status = 'Y'";    

完成日期后,我会像这样做两个循环

foreach ($dates as $key => $dt) {       
    foreach ($resp as $InstNo => $InstRow) {
        try {
            $stmt2->execute(array(':dt' => $dt, ':inid' => $InstRow->in_id, ':ipid' => $InstRow->ip_id));
            $rowDb = $stmt2->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_NEXT);
        } catch(PDOException $e) {
            echo '{"error":{"text":"'. $e->getMessage() .'"}}'; 
        }
    }
}

首先,它开始循环日期,然后开始循环标题(基于在获取日期之前进行的查询)。我的问题一直困在这里

$stmt2->execute(array(':dt' => $dt, ':inid' => $InstRow->in_id, ':ipid' => $InstRow->ip_id));
你怎么看?有没有更好的方法来解决这个问题?

有关您的信息,我使用Slim和PHPExcel。 PHPExcel可能存在内存问题,我想切换到Spout,但文档仍然是关于基本内容的。

3 个答案:

答案 0 :(得分:6)

在SQL中,您可以考虑使用limit clause来减轻内存负载,如下所示:

$handle = fopen("file.csv", "wb");
$statement = "
SELECT  instruments.in_id, instrument_parameters.ip_id,
       CASE WHEN gv_x_ipid = -1 THEN 'datetime' ELSE '' END xlabel,
       CASE WHEN ip_label LIKE '%Reservoir%' THEN 0 ELSE in_order END legendIndex,
       CASE WHEN in_name = 'General' THEN ip_label ELSE in_name END ylabel
    FROM  graph_plot
    LEFT JOIN  attributes gptype ON gp_type = gptype.at_id
    LEFT JOIN  graph_value ON gp_id = gv_gpid
    LEFT JOIN  instrument_parameters ON gv_y_ipid = ip_id
    LEFT JOIN  attributes pmunit ON ip_unit = pmunit.at_id
    LEFT JOIN  instrument_reading yvalue ON gv_y_ipid = iv_ipid
    LEFT JOIN  instruments ON iv_inid = in_id
    WHERE  gp_diid = :di_id
      AND  gp_type = :rpt_type
      AND  iv_status = 'Y'
      AND  iv_inid in (".implode(",", $coll->inid).")
      AND  gv_y_ipid in (".implode(",", $coll->ipid).")
    GROUP BY  ylabel
    ORDER BY  legendIndex
    LIMIT  250
";
$prep = $dbh->prepare($statement);
for ($i = 0; $prep -> rowCount < 250; $i+= 250) {
    fputcsv(prep->fetchAll());
    $prep = $dbh->prepare($statement.' OFFSET'.$i);
}
fclose($handle);

或者,您可以使用system并致电SELECT INTO,设置权限(如有必要),鲍勃是您的叔叔。

答案 1 :(得分:5)

您尚未终止filter循环。

fetch

得到&#34; next&#34;排列或关闭游标&#39;并终止。

您是否希望获得一排?如果是这样,请考虑进行fetchAll。 (注意:结果集在数组中可能是一个额外的级别。)

答案 2 :(得分:0)

PDO MySQL 驱动程序会做一些缓冲,这会在循环大型数据集时导致内存耗尽。您可以使用 $pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); 关闭此功能,这应该可以解决问题。

$pdo = new PDO('mysql:localhost', $username, $password);
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

$stmt = $pdo->prepare('SELECT * FROM instrument...');
$stmt->execute($parameters);

while($row = $stmt->fetch()) {
    // Insert logic to write the row to the destination
}

如果您只想为该查询设置属性,您也可以这样做:

$stmt = $pdo->prepare('SELECT * FROM instrument...', [
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
]);

请记住,在完成未缓冲的查询之前,您将无法运行其他查询。如果不需要剩余的结果,可以使用 $stmt->closeCursor() 提前关闭旧游标。我也无法谈论它的性能,但它在编写一次性脚本时解决了我的问题。

MySQL 的文档中简要提到了该设置: https://dev.mysql.com/doc/connectors/en/apis-php-pdo-mysql.html