【问题标题】:php pdo efficient way to insert too many rows at a time [closed]php pdo一次插入太多行的有效方法[关闭]
【发布时间】:2015-05-24 09:04:19
【问题描述】:

我正在以编程方式将行从 excel 文件导入数据库。 excel 文件有 10 列 * 30000 行。我已经在 php 数组中导入了这些数据,然后将其插入到数据库中。

上传文件后,将所有行插入数据库需要 7-8 分钟。我知道插入行的两种方法

第一种方法:

生成类似的动态查询,

INSERT INTO table_name (col1, col2,..) VALUES (row1, row1, ....), (row2, row2, ....), (row3, row3, ....), (row4, row4, ....), ...

并运行查询以插入所有行。

第二种方法:

$con->prepare("INSERT INTO table_name (col1, col2, ...) VALUES (:col1, :col2, ...)");
foreach ($rows as $row) { // Loop through all rows and insert them
    $result->bindParam(':col1', $val1);
    $result->bindParam(':col2', $val2);
    $result->bindParam(':col3', $val3);
    ...
    ...
    $result->execute();
}

第一种方法看起来混乱且效率低下,我使用的是第二种方法,但它每秒只插入 500-700 行,插入所有行总共需要 7-8 分钟。

还有哪些比这些更高效、更快的方法?

编辑:不建议将excel文件直接导入mysql。数据在插入数据库之前需要进行处理。

【问题讨论】:

  • 转换为 CSV 然后使用LOAD DATA INFILE
  • 请查看已编辑的问题。
  • 你能把它包装成一个交易吗? COMMITing 数据使用资源,如果您启用了自动提交,则必须为每个 INSERT 执行此操作 - 检查 this link - 您也必须在 PDO 中禁用自动提交 - check here - 不要如果您采用这种方法,请不要忘记在最后COMMIT您的数据(不过,对于您正在尝试做的事情,我绝对会推荐)
  • 谢谢。我会在代码中手动尝试COMMIT

标签: php mysql database pdo


【解决方案1】:

最快的方法是将数百个插入包装到一个事务和提交中。 MySQL 将使用硬盘驱动器的单个输入/输出操作来写入许多记录 - 这很快。

$con->prepare("INSERT INTO table_name (col1, col2, ...) VALUES (:col1, :col2, ...)");

$inserts = 1000;
$counter = 0;

foreach ($rows as $row) { // Loop through all rows and insert them

    if($counter === 0 && !$con->inTransaction()) {
        $con->beginTransaction();
    }

    $result->bindParam(':col1', $val1);
    $result->bindParam(':col2', $val2);
    $result->bindParam(':col3', $val3);
    ...
    ...
    $result->execute();

    $counter++;

    if($counter === $inserts) {
        $con->commit();
    }    
}

if($con->inTransaction()) {
    $con->commit();
    $counter = 0;
}

上面的代码应该在每执行 1000 次插入后提交。我没有测试它,所以它很可能包含错误,但它的目的是说明如何在事务中包装 1000 个插入并提交它。

【讨论】:

  • if( ($counter % $inserts ) == 0 ) 应该使用模数,否则它只会执行一次.. 像这样的东西:if(($counter % $inserts)==0) { ...
【解决方案2】:

尝试将您的 SQL 封装到一个 TRANSACTION 中,最后提交您的数据。这将释放大量资源,因为 COMMITING 数据做了很多工作(它告诉您的数据库保存您正在插入的数据和 REINDEX - 假设您拥有它们)。

另外,请尝试确保您与数据库的连接是池化的 - 这意味着您不会每次都从数据库中获取新连接,而是使用相同的连接。

不过,使用事务的风险在于,如果数据集中出现一个错误(阻止 INSERT),它将回滚整个数据集。

这样的东西可以工作..

<?php
  try {
    $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2', 
      array(PDO::ATTR_PERSISTENT => true)); // POOLED
      echo "Connected\n";
  } catch (Exception $e) {
    die("Unable to connect: " . $e->getMessage());
  }

  try {  
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $dbh->beginTransaction();
    $dbh->prepare("INSERT INTO table_name (col1, col2, ...) VALUES (:col1, :col2, ...)");
    foreach ($rows as $row) { // Loop through all rows and insert them

      // I am not sure where you define $result
      // Review in your implementation if you use
      $result->bindParam(':col1', $val1);
      $result->bindParam(':col2', $val2);
      $result->bindParam(':col3', $val3);
      ...
      ...
      $result->execute();
    }

  } catch (Exception $e) {
    $dbh->rollBack();
    echo "Failed: " . $e->getMessage();
  }


  $dbh->commit();

?>

【讨论】:

  • @N.B.答案要好得多 - 不过,请考虑 POOLING 连接。
  • 如果我不使用 array(PDO::ATTR_PERSISTENT =&gt; true)); 会有什么不同吗?因为我使用的是数据库配置文件,我猜不是所有的查询都需要它。
  • 您不必在上面的示例中这样做,因为无论如何它都会使用相同的连接 - 但是,如果您要在问题中使用原始示例,那么我会考虑它,是的。
  • 一篇很好的帖子 here 来看看 SO。还有here在维基百科上
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-14
  • 2014-12-14
相关资源
最近更新 更多