codeigniter 有关从返回错误的大型查询返回结果的建议-已用尽允许的内存大小734003200字节

tzdcorbm  于 2022-12-07  发布在  其他
关注(0)|答案(2)|浏览(123)

我试图返回3个表连接在一起的结果,以便用户以CSV格式下载,这将引发错误:
已用尽允许的内存大小734003200字节
这是正在运行的查询:

SELECT *
FROM `tblProgram`
JOIN `tblPlots` ON `tblPlots`.`programID`=`tblProgram`.`pkProgramID`
JOIN `tblTrees` ON `tblTrees`.`treePlotID`=`tblPlots`.`id`

导致错误的代码行如下:

$resultsALL=$this->db->query($fullQry);

其中$fullQry是上面显示的查询。当我注解掉这一行时,所有的东西都没有错误地运行。所以我确信这不是一个我遗漏的无限循环。
我想知道如何分解查询,以便可以在不出错的情况下获得结果?表中目前只有相对少量的数据,而且最终会变得更大,因此我认为提高内存大小不是一个好的选择。
我正在使用CodeIgniter/php/mysql。如果需要的话,我可以提供更多的代码...
谢谢你的任何方向,你可以建议!

yzxexxkh

yzxexxkh1#

基于:MySQL : retrieve a large select by chunks
您也可以尝试使用LIMIT子句来撷取区块中的数据。
由于您使用的是CodeIgniter 3,下面是您可以使用它的方法。
如果联接的表具有冲突的id列名,则可能需要将不同的$orderBy * 参数#6 * 传递给getChunk(...)方法。
即:$this->getChunk(..., ..., ..., 0, 2000, "tblProgram.id");

解决方案:
<?php

class Csv_model extends CI_Model
{
    public function __construct()
    {
        parent::__construct();
        $this->load->database();
    }

    public function index()
    {
        $sql = <<< END
SELECT *
FROM `tblProgram`
JOIN `tblPlots` ON `tblPlots`.`programID`=`tblProgram`.`pkProgramID`
JOIN `tblTrees` ON `tblTrees`.`treePlotID`=`tblPlots`.`id`
END;

        $this->getChunk(function (array $chunk) {
            /*
             * Do something with each chunk here;
             * Do something with each chunk here;
             * log_message('error', json_encode($chunk));
             * */
        }, $this->db, $sql);
    }

    /*
     * Processes a raw SQL query result in chunks sending each chunk to the provided callback function.
     * */
    function getChunk(callable $callback, $DBContext, string $rawSQL = "SELECT 1", int $initialRowOffset = 0, int $maxRows = 2000, string $orderBy = "id")
    {
        $DBContext->query('DROP TEMPORARY TABLE IF EXISTS chunkable');
        $DBContext->query("CREATE TEMPORARY TABLE chunkable AS ( $rawSQL ORDER BY `$orderBy` )");

        do {

            $constrainedSQL = sprintf("SELECT * FROM chunkable ORDER BY `$orderBy` LIMIT %d, %d", $initialRowOffset, $maxRows);
            $queryBuilder = $DBContext->query($constrainedSQL);
            $callback($queryBuilder->result_array());
            $initialRowOffset = $initialRowOffset + $maxRows;

        } while ($queryBuilder->num_rows() === $maxRows);
    }

}
q3qa4bjr

q3qa4bjr2#

使用getUnbufferedRow()处理大型结果集。
getUnbufferedRow()
此方法返回单个结果行,而不像row()那样在内存中预取整个结果。如果查询有多行,它将返回当前行并将内部数据指针向前移动。

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

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

为了与MySQLi一起使用,您可以将MySQLi的结果模式设置为MYSQLI_USE_RESULT,以最大限度地节省内存。通常不建议使用此模式,但在某些情况下(如将大型查询写入csv)它可能很有用。如果您更改结果模式,请注意与其相关的权衡。

$db->resultMode = MYSQLI_USE_RESULT; // for unbuffered results

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

$file = new \CodeIgniter\Files\File(WRITEPATH.'data.csv');

$csv = $file->openFile('w');

while ($row = $query->getUnbufferedRow('array'))
{
    $csv->fputcsv($row);
}

$db->resultMode = MYSQLI_STORE_RESULT; // return to default mode
注:

使用MYSQLI_USE_RESULT时,在提取所有记录或进行freeResult()调用之前,同一连接上的所有后续调用都将导致错误。getNumRows()方法将仅返回基于数据指针当前位置的行数。MyISAM表将保持锁定,直到提取所有记录或进行freeResult()调用。
您可以选择性地传递'object'(预设值)或'array',以指定传回值的型别:

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

freeResult()
它释放与结果相关的内存并删除结果资源ID。通常PHP会在脚本执行结束时自动释放内存。但是,如果您在特定脚本中运行大量查询,您可能希望在每个查询结果生成后释放结果,以减少内存消耗。

$query = $thisdb->query('SELECT title FROM my_table');

foreach ($query->getResult() as $row) {
    echo $row->title;
}

$query->freeResult(); // The $query result object will no longer be available

$query2 = $db->query('SELECT name FROM some_table');

$row = $query2->getRow();
echo $row->name;
$query2->freeResult(); // The $query2 result object will no longer be available

相关问题