【问题标题】:Import Large CSV file into MySQL将大型 CSV 文件导入 MySQL
【发布时间】:2012-09-19 06:37:54
【问题描述】:

我正在尝试将 csv 文件导入到 mysql 表中,我目前有一个正在逐行运行的脚本,因为我需要将一个 id 与另一个 id 结合起来进行哈希处理,并将日期格式化为 mysql 格式。

csv 文件的列比我当前导入的要多。只导入所有列更容易吗?

我正在阅读有关 LOAD DATA INFILE (http://dev.mysql.com/doc/refman/5.1/en/load-data.html) 的信息,但我想知道如何使用它并散列 id 和格式化日期而不进行逐行执行。我当前的脚本运行时间过长,导致运行时出现网站性能问题。

这是我所拥有的:

$url = 'http://www.example.com/directory/file.csv';
if (($handle = fopen($url, "r")) !== FALSE) 
{
fgetcsv($handle, 1000, ",");
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) 
{
    $EvID = $data[0];
    $Ev = $data[1];
    $PerID = $data[2];
    $Per = $data[3];
    $VName = $data[4];
    $VID = $data[5];
    $VSA = $data[6];
    $DateTime = $data[7];
    $PCatID = $data[8];
    $PCat = $data[9];
    $CCatID = $data[10];
    $CCat = $data[11];
    $GCatID = $data[12];
    $GCat = $data[13];
    $City = $data[14];
    $State = $data[15];
    $StateID = $data[16];
    $Country = $data[17];
    $CountryID = $data[18];
    $Zip = $data[19];
    $TYN = $data[20];
    $IMAGEURL = $data[21];
    $URLLink = $data[22];

        $data[7] = strtotime($data[7]);
        $data[7] = date("Y-m-d H:i:s",$data[7]);

    if((($PCatID == '2') && (($CountryID == '217') or ($CountryID == '38'))) || (($GCatID == '16') or ($GCatID == '19') or ($GCatID == '30') or ($GCatID == '32'))) 
    {
            if(!mysql_query("INSERT IGNORE INTO TNDB_CSV2 
                (id, EvID, Event, PerID, Per, VName,
                     VID, VSA, DateTime, PCatID, PCat,                
                CCatID, CCat, GCatID, GCat, City,
                     State, StateID, Country, CountryID, Zip,
                TYN, IMAGEURL) VALUES
                ('".md5($EventID.$PerformerID)."','".addslashes($data[0])."','".addslashes($data[1])."','".addslashes($data[2])."','".addslashes($data[3])."','".addslashes($data[4])."',
                    '".addslashes($data[5])."','".addslashes($data[6])."','".addslashes($data[7])."','".addslashes($data[8])."','".addslashes($data[9])."',
                '".addslashes($data[10])."','".addslashes($data[11])."','".addslashes($data[12])."','".addslashes($data[13])."','".addslashes($data[14])."',
                    '".addslashes($data[15])."','".addslashes($data[16])."','".addslashes($data[17])."','".addslashes($data[18])."','".addslashes($data[19])."',
                '".addslashes($data[20])."','".addslashes($data[21])."')"))
            {                    
                exit("<br>" . mysql_error());
            }
    }
}
fclose($handle);
}

我们非常感谢任何帮助。提前致谢。

【问题讨论】:

    标签: php mysql load-data-infile fgetcsv


    【解决方案1】:

    尝试使用implode() 函数进行批量插入。进一步的解释和示例,请参阅此线程insert multiple rows via a php array into mysql

    【讨论】:

    • 谢谢,我会再调查一下。
    【解决方案2】:

    首先尝试优化您的脚本。首先,除非您别无选择,否则在导入时切勿运行单个查询,网络开销可能是一个杀手。

    尝试类似(显然未经测试并在 SO 文本框中编码,检查括号匹配 e.c.t.):

    $url = 'http://www.example.com/directory/file.csv';
    if (($handle = fopen($url, "r")) !== FALSE) 
    {
    fgetcsv($handle, 1000, ",");
    
    $imports = array();
    
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) 
    {
        $EvID = $data[0];
        $Ev = $data[1];
        $PerID = $data[2];
        $Per = $data[3];
        $VName = $data[4];
        $VID = $data[5];
        $VSA = $data[6];
        $DateTime = $data[7];
        $PCatID = $data[8];
        $PCat = $data[9];
        $CCatID = $data[10];
        $CCat = $data[11];
        $GCatID = $data[12];
        $GCat = $data[13];
        $City = $data[14];
        $State = $data[15];
        $StateID = $data[16];
        $Country = $data[17];
        $CountryID = $data[18];
        $Zip = $data[19];
        $TYN = $data[20];
        $IMAGEURL = $data[21];
        $URLLink = $data[22];
    
            $data[7] = strtotime($data[7]);
            $data[7] = date("Y-m-d H:i:s",$data[7]);
    
        if((($PCatID == '2') && (($CountryID == '217') or ($CountryID == '38'))) || (($GCatID == '16') or ($GCatID == '19') or ($GCatID == '30') or ($GCatID == '32'))) 
        {
    
        $imports[] = "('".md5($EventID.$PerformerID)."','".addslashes($data[0])."','".addslashes($data[1])."','".addslashes($data[2])."','".addslashes($data[3])."','".addslashes($data[4])."',
                        '".addslashes($data[5])."','".addslashes($data[6])."','".addslashes($data[7])."','".addslashes($data[8])."','".addslashes($data[9])."',
                    '".addslashes($data[10])."','".addslashes($data[11])."','".addslashes($data[12])."','".addslashes($data[13])."','".addslashes($data[14])."',
                        '".addslashes($data[15])."','".addslashes($data[16])."','".addslashes($data[17])."','".addslashes($data[18])."','".addslashes($data[19])."',
                    '".addslashes($data[20])."','".addslashes($data[21])."')";
    
    
    
        }
    }
    
    $importarrays = array_chunk($imports, 100);
    foreach($importarrays as $arr) {
    
     if(!mysql_query("INSERT IGNORE INTO TNDB_CSV2 
                    (id, EvID, Event, PerID, Per, VName,
                         VID, VSA, DateTime, PCatID, PCat,                
                    CCatID, CCat, GCatID, GCat, City,
                         State, StateID, Country, CountryID, Zip,
                    TYN, IMAGEURL) VALUES ".implode(',', $arr)){
    
         die("error: ".mysql_error());
    
     }
    
     }
    
    fclose($handle);
    }
    

    使用array_chunk中的数字,太大,可能会导致查询太长(是的,my.cnf中有一个可配置的限制),太小和不必要的开销等问题。

    您也可以放弃使用将 $data[x] 分配给变量的做法,因为脚本很小,只需在查询中直接使用 $data[x] 即可。 (不会有很大的改进,但根据您的导入大小,它可以节省一点)。

    接下来是使用低优先级的插入/更新,请查看此以获得更多信息以帮助您入门:How to give priority to certain queries?

    毕竟,您可能会想到 mysql 配置优化,但这是 google 真正解释的一个,因为每个人及其独特情况的最佳设置都不同

    编辑:我之前做过的另一件事是,如果您设置了很多导入时不需要的密钥,您可以暂时删除这些密钥并在脚本完成。这也可以带来很好的时间改进,但是当你在一个实时数据库上工作时,如果你走这条路,就会有一些陷阱需要解决。

    【讨论】:

    • 感谢李的建议,您认为这会产生重大影响吗?
    • 应该,测试一下,看看(谷歌有一个 php 计时器脚本,如果你愿意,你可以量化它,或者查看 php.net 上的 microtime() cmets,确定那里有你可以复制粘贴)。批处理具有相同插入结构的查询是我在慢速脚本上做的第一件事。但是,实际节省的时间取决于很多因素。
    • 现在只需不到 30 秒,而不是 15 分钟以上。感谢您的帮助!
    【解决方案3】:

    我使用了这个查询

    $sql = "
            LOAD DATA LOCAL INFILE 'uploads/{$fileName}'
            REPLACE INTO TABLE `order`
            FIELDS
                TERMINATED BY '\t'
            LINES
                TERMINATED BY '\r\n'
            IGNORE 1 LINES
            (product_id, `date`, quantity)
            ";
    

    速度很快

    【讨论】:

      猜你喜欢
      • 2014-01-06
      • 1970-01-01
      • 2015-02-24
      • 2019-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多