You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

sqlsrv_fetch_array遍历中断:SQL Server转MySQL脚本异常终止问题

排查PHP读取SQL Server大结果集时静默终止的问题

看起来你遇到了一个挺棘手的无声故障——明明sqlsrv_num_rows返回了正确的16万行总数,但脚本处理到5万条左右就悄悄终止了,连个错误提示都没有。这种情况通常和资源限制、驱动行为或者隐性的超时有关,我来给你几个排查和解决的方向:

1. 检查PHP的执行超时与内存限制

处理16万条数据是个耗时且占内存的操作,PHP默认的执行时间(max_execution_time通常30/60秒)和内存限制(memory_limit通常128M)可能不足以支撑,到阈值后脚本会直接终止,且如果没开启错误输出可能看不到任何提示。

你可以在脚本开头临时调整这些参数:

// 取消执行时间限制(0表示无限制)
ini_set('max_execution_time', 0);
// 调整内存限制为512M,根据实际情况可适当增大
ini_set('memory_limit', '512M');

2. 避免使用可滚动游标,改用流式或分批读取

你当前使用了Scrollable => 'static'静态游标,虽然能正确获取总行数,但静态游标在处理超大结果集时,驱动可能会在后台缓存大量数据,容易引发隐性的资源耗尽或读取中断。

方案一:改用默认顺向游标+实时监控

去掉Scrollable参数,使用默认的顺向游标(只能向前读取),同时增加批量日志输出,方便定位终止位置:

$tsql= "SELECT * FROM ordini_all ORDER BY id ASC";
// 去掉Scrollable参数,使用默认顺向游标
$getResults= sqlsrv_query($msconn, $tsql);
if ($getResults === FALSE){ die(FormatErrors(sqlsrv_errors()));} 
else{echo "ok query - Righe " . sqlsrv_num_rows($getResults) . "\n\n\n";}

// ... 其余数据库连接代码不变 ...

while($row = sqlsrv_fetch_array($getResults, SQLSRV_FETCH_ASSOC)) {
    $counter++;
    // 每处理1000条输出一次进度,强制刷新缓冲区确保能看到实时日志
    if ($counter % 1000 === 0) {
        echo "已处理 {$counter} 条记录...\n";
        ob_flush();
        flush();
    }
    // ... 你的插入逻辑 ...
}

方案二:使用SQL分页查询,分批处理

通过OFFSET ... FETCH NEXT语法分批次读取数据,避免一次性加载所有结果到内存,同时每次处理完一批就释放资源:

$batchSize = 1000; // 每批次处理1000条
$offset = 0;
$totalProcessed = 0;

do {
    $tsql = "SELECT * FROM ordini_all ORDER BY id ASC OFFSET {$offset} ROWS FETCH NEXT {$batchSize} ROWS ONLY";
    $getResults = sqlsrv_query($msconn, $tsql);
    if ($getResults === FALSE) {
        die(FormatErrors(sqlsrv_errors()));
    }

    $batchCount = 0;
    while ($row = sqlsrv_fetch_array($getResults, SQLSRV_FETCH_ASSOC)) {
        // ... 你的插入逻辑 ...
        $batchCount++;
        $totalProcessed++;
    }

    sqlsrv_free_stmt($getResults); // 释放当前批次的资源
    $offset += $batchSize;
    echo "累计处理 {$totalProcessed} 条记录...\n";
} while ($batchCount === $batchSize); // 当批次处理数小于设定值时,说明已处理完所有数据

3. 显性检查MySQL写入错误

mysqli默认不会主动抛出异常,即使插入失败也可能静默跳过,你需要手动检查每次插入的结果:

// 假设你的插入语句示例
$insertSql = "INSERT INTO your_mysql_table (col1, col2) VALUES (?, ?)";
$stmt = $mysql_conn->prepare($insertSql);
$stmt->bind_param("ss", $row['col1'], $row['col2']); // 根据实际字段类型调整

if (!$stmt->execute()) {
    $err_counter++;
    echo "插入第 {$counter} 条记录失败:" . $stmt->error . "\n";
    // 可选:记录错误到日志,或者继续处理下一条
    continue;
}
$stmt->close();

另外,也可以检查MySQL的max_allowed_packet参数,如果插入的字段包含大文本/二进制数据,这个参数太小也可能导致静默失败。

4. 检查系统级资源限制

如果服务器的CPU、内存占用过高,系统可能会直接终止PHP进程,这种情况PHP不会输出错误,需要查看服务器的系统日志:

  • Linux:查看/var/log/syslog或/var/log/messages
  • Windows:打开事件查看器,查看应用程序日志

5. 开启PHP错误日志排查隐性错误

有些错误不会输出到控制台,但会写入PHP错误日志,你可以在脚本开头开启日志:

ini_set('log_errors', 1);
ini_set('error_log', '/path/to/your/php-error.log'); // 替换为实际路径

运行脚本后查看日志文件,可能会找到导致终止的隐性错误信息。


你提供的原始代码片段:

$tsql= "SELECT * FROM ordini_all ORDER BY id ASC";
$getResults= sqlsrv_query($msconn, $tsql, array(), array( "Scrollable" => 'static' ));
if ($getResults === FALSE){ die(FormatErrors(sqlsrv_errors()));} else{echo "ok query - Righe " . sqlsrv_num_rows($getResults) . "\n\n\n";}
// Create connection - to local mysql db
$mysql_conn = new mysqli($servername, $username, $password, $dbname);
if ($mysql_conn->connect_error) { die("Connection failed: " . $mysql_conn->connect_error);}
$inizio = date("h:i:sa");
$counter = 0;
$err_counter = 0;
$tot_orders = sqlsrv_num_rows($getResults);
echo "\n Rows " . $tot_orders; //this is the correct number
while($row = sqlsrv_fetch_array($getResults, SQLSRV_FETCH_ASSOC)) {
... do your things to insert rows in another db
}
echo "\nInizio " . $inizio . " Fine " . date("h:i:sa") . " tot err...";

内容的提问来源于stack exchange,提问作者sissy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:02:37