将大型CSV导入mysql数据库

时间:2011-08-03 20:16:57

标签: php mysql csv

我在尝试将大型CSV文件导入localhost上的mysql时非常麻烦。

CSV大约为55 MB,大约有750,000行。

我重写了脚本,以便解析CSV并逐个转储行。

以下是代码:

$row = 1;
if (($handle = fopen("postal_codes.csv", "r")) !== FALSE) 
{
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) 
    {
        $num = count($data);
        $row++;
        for ($c=0; $c < $num; $c++) 
        {
            $arr = explode('|', $data[$c]);

            $postcode = mysql_real_escape_string($arr[1]);
            $city_name = mysql_real_escape_string($arr[2]);
            $city_slug = mysql_real_escape_string(toAscii($city_name));
            $prov_name = mysql_real_escape_string($arr[3]);
            $prov_slug = mysql_real_escape_string(toAscii($prov_name));
            $prov_abbr = mysql_real_escape_string($arr[4]);
            $lat = mysql_real_escape_string($arr[6]);
            $lng = mysql_real_escape_string($arr[7]);

            mysql_query("insert into cities (`postcode`, `city_name`, `city_slug`, `prov_name`, `prov_slug`, `prov_abbr`, `lat`, `lng`) 
                         values ('$postcode', '$city_name', '$city_slug', '$prov_name', '$prov_slug', '$prov_abbr', '$lat', '$lng')") or die(mysql_error());
        }
    }
    fclose($handle);
}

问题是它需要永远执行。任何熟悉的解决方案都将受到赞赏。

7 个答案:

答案 0 :(得分:6)

你正在重新发明轮子。查看MySQL附带的mysqlimport工具。它是导入CSV数据文件的有效工具。

mysqlimport是LOAD DATA LOCAL INFILE SQL语句的命令行界面。

要么比逐行INSERT快10-20倍。

答案 1 :(得分:2)

您的问题很可能是您自动提交(默认情况下),因此MySQL为每个插入提交了一个新事务。您应该使用SET autocommit=0;关闭自动提交。如果你可以切换到使用mysqli库(如果可能的话,你可以使用),你可以使用mysqli::autocommit(false)来关闭自动提交。

$mysqli = new mysqli('localhost','db_user','my_password','mysql');
$mysqli->autocommit(false);
$stmt=$mysqli->prepare("insert into cities (`postcode`, `city_name`, `city_slug`, `prov_name`, `prov_slug`, `prov_abbr`, `lat`, `lng`) 
                     values (?, ?, ?, ?, ?, ?, ?, ?);")


$row = 1;
if (($handle = fopen("postal_codes.csv", "r")) !== FALSE) 
{
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) 
    {
        $num = count($data);
        $row++;
        for ($c=0; $c < $num; $c++) 
        {
            $arr = explode('|', $data[$c]);
            $stmt->bind_param('ssssssdd', $arr[1], $arr[2], toAscii(arr[2]), $arr[3], toAscii($arr[3]), $arr[4], $arr[6], $arr[7]);
            $stmt->execute();
        }
    }
}
$mysqli->commit();
fclose($handle);

答案 2 :(得分:2)

如果可以

,使用LOAD DATA要快得多

答案 3 :(得分:1)

尝试在一个查询中执行此操作。

虽然

可能会受到my.cnf(mysql配置)的限制
<?php

$row = 1;
$query = ("insert into cities ");
if (($handle = fopen("postal_codes.csv", "r")) !== FALSE) 
{
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) 
    {
        $num = count($data);
        $row++;
        for ($c=0; $c < $num; $c++) 
        {
            $arr = explode('|', $data[$c]);

            $postcode = mysql_real_escape_string($arr[1]);
            $city_name = mysql_real_escape_string($arr[2]);
            $city_slug = mysql_real_escape_string(toAscii($city_name));
            $prov_name = mysql_real_escape_string($arr[3]);
            $prov_slug = mysql_real_escape_string(toAscii($prov_name));
            $prov_abbr = mysql_real_escape_string($arr[4]);
            $lat = mysql_real_escape_string($arr[6]);
            $lng = mysql_real_escape_string($arr[7]);
            $query .= "(`postcode`, `city_name`, `city_slug`, `prov_name`, `prov_slug`, `prov_abbr`, `lat`, `lng`) 
                         values ('$postcode', '$city_name', '$city_slug', '$prov_name', '$prov_slug', '$prov_abbr', '$lat', '$lng'),";

        }
    }
    fclose($handle);
}
mysql_query(rtrim($query, ","));

如果它不起作用,你可以尝试这个(禁用自动提交)

mysql_query("SET autocommit = 0");
$row = 1;
if (($handle = fopen("postal_codes.csv", "r")) !== FALSE) 
{
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) 
    {
        $num = count($data);
        $row++;
        for ($c=0; $c < $num; $c++) 
        {
            $arr = explode('|', $data[$c]);

            $postcode = mysql_real_escape_string($arr[1]);
            $city_name = mysql_real_escape_string($arr[2]);
            $city_slug = mysql_real_escape_string(toAscii($city_name));
            $prov_name = mysql_real_escape_string($arr[3]);
            $prov_slug = mysql_real_escape_string(toAscii($prov_name));
            $prov_abbr = mysql_real_escape_string($arr[4]);
            $lat = mysql_real_escape_string($arr[6]);
            $lng = mysql_real_escape_string($arr[7]);

            mysql_query("insert into cities (`postcode`, `city_name`, `city_slug`, `prov_name`, `prov_slug`, `prov_abbr`, `lat`, `lng`) 
                         values ('$postcode', '$city_name', '$city_slug', '$prov_name', '$prov_slug', '$prov_abbr', '$lat', '$lng')") or die(mysql_error());
        }
    }
    fclose($handle);
}

答案 4 :(得分:1)

我使用SQL服务器执行此操作:

  • 我使用SQL Bulkinsert命令结合数据表。
  • 数据表驻留在内存中,并通过读取文件内的行来构建。
  • 每个数据表都是从一大块行构建的,而不是整个文件。
  • 通过保持指针从最后一行读取和最大块大小来跟踪处理的块。
  • 当您阅读文件时。当行id&gt;退出循环时最后一行+块大小。
  • 保持循环并继续插入。

答案 5 :(得分:0)

有时当您使用加载数据时,如果有警告,导入将停止。您可以使用关键字ignore。

LOAD DATA INFILE 'file Path' IGNORE INTO TABLE YOUR_Table

答案 6 :(得分:0)

我有类似的情况,使用LOAD DATA是不可行的。交易有时也是不可接受的,因为需要检查重复数据。然而,以下内容大大改善了我的一些导入数据文件的处理时间。

在while循环(CSV Lines)之前,将autocommit设置为0并启动事务(仅限InnoDB):

mysql_query('SET autocommit=0;');
mysql_query('START TRANSACTION;');

循环后,提交并重置自动提交回1(默认值):

mysql_query('COMMIT;');
mysql_query('SET autocommit=1;');

将mysql_query()替换为您的代码使用的任何数据库对象。我希望这有助于其他人。