如何使用PHP将大型CSV文件上传到MySQL而没有重复的条目?

时间:2016-04-19 11:38:23

标签: php mysql csv

我需要上传一个大的CSV到MySQL,如果上传中断,我必须从我停止的地方开始,没有重复的条目。如果进程中断,那么它应该从我自动结束的地方重新开始(即:如果在123个条目之后上传breaK,那么它将在下一次运行时从124恢复)

CSV文件格式:

latitude longitude 
6.5486   72.456
4.2186   74.466
5.5486   82.956

我只需要一个具有相同纬度和经度的条目,目前我正在使用下面的代码(正常工作),但我不知道如果上传中断,如何从断点开始。

<?php  
error_reporting(0);
require("connection.php");//connect to the database
if ($_FILES[csv][size] > 0){
    //get the csv file
    $file = $_FILES[csv][tmp_name];
    echo $fname = $_FILES['csv']['name'];
    echo $ftype = end(explode('.', strtolower($fname)));
    if($ftype=="csv"){
        $handle = fopen($file,"r"); 
        //loop through the csv file and insert into database 
        do { 
        if ($data[0]) { 
         $latitude=$data[0];
         $longitude=$data[1];
         $location1=$data[2];
         $location2=$data[3];
         $location3=$data[4];
         $sql = "SELECT * FROM latitude_longitude WHERE latitude ='$latitude' AND longitude='$longitude' ";
         $result=mysql_query($sql);
         if( mysql_num_rows($result) > 0){
           mysql_query("UPDATE latitude_longitude SET latitude = '$latitude',longitude = '$longitude',location1='$location1', location2='$location2',location3='$location3',status=status+1 WHERE latitude = '$latitude' AND longitude = '$longitude'");
        }
        else{
             mysql_query("INSERT INTO latitude_longitude (latitude, longitude, location1, location2, location3, status, date) VALUES 
            ( 
                '".addslashes($data[0])."', 
                '".addslashes($data[1])."', 
                '".addslashes($data[2])."',
                '".addslashes($data[3])."', 
                '".addslashes($data[4])."', 
                '1',
                CURRENT_TIMESTAMP
            ) 
        "); 
        }
        } 
        } while ($data = fgetcsv($handle,1000,",","'")); 
        //redirect 
        header('Location:GeoLocation.php?success=1'); die; 

     }else{
         header('Location:GeoLocation.php?success=2'); die; 

     }  
} 
?> 

感谢提前帮助。

2 个答案:

答案 0 :(得分:1)

在经度,纬度上创建唯一键

然后你可以使用下面的东西

LOAD DATA LOCAL INFILE 'c:\\temp\\filename.csv'
replace
INTO TABLE table_name
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 1 LINES
(@col1,@col2,@col3)
set
column1 = @col1,
column2= @col2,
column3= @col3;

这将替换重复的long,lat和insert new

答案 1 :(得分:0)

注意:您建议强烈使用PDO,因为mysql_*功能正在逐步淘汰。你也可以摆脱所有addslashes()

对于插入,您可以这样继续:

保持会话有关CSV的一些信息

$import = array(
    'current' => 0,
    'total'   => 0, // Estimated
    'begun'   => 0,
    'fpos'    => 0,
    'flen'    => 0, // Total CSV file size
    'errors'  => 0,
);

并创建一个UNIQUE INDEX,覆盖您不想重复的字段(CREATE UNIQUE INDEX ...

从CSV导入时:

- open $csv file
- fseek() the file to the $session['fpos'] offset
- MySQL set AUTOCOMMIT to off;
- MySQL BEGIN WORK;
- Get current time plus 10 seconds into $ttl
- loop
    - read one record using, say, fgetcsv()
    - try
        - insert into the DB using INSERT IGNORE
    - catch PDO error
        - MySQL ROLLBACK, $session['errors']++ and immediately die().
    - is time() equal or above $ttl? If so, break
- update the session object, set its 'errors' to 0, put fpos() of $csv file into it
- MySQL COMMIT;
- Your ETA is $session['begun'] + (time()-$session['begun'])*($session['fpos']/$session['flen']).

使用上述方法,每次迭代需要10秒以上。

如果插入顺利,新会话将包含从哪里开始的下一个文件偏移量。

如果出现一些错误,整个事务块将回滚,就好像它从未启动过一样。你跟踪连续的错误(它们可能意味着CSV中的错误)。

您可以执行此操作并将会话对象作为JSON返回。然后,您可以通过jQuery $.get()导入调用上述脚本的CSV,并使用它来更新进度条。您将能够计算整个过程的预计到达时间:

 importing [##############                   ] 48%, 32m 15s left

这意味着&#34; UPLOAD&#34;页面必须将CSV文件移动到临时目录中,并立即显示将显示进度条的HTML。然后这个HTML(和Javascript)将负责继续上传。

还有上传文件库,例如 PLupload ,可以在&#34; chunks&#34;上传文件。并显示自己的进度条。由于网络上传通常比MySQL上传,因此一次上传一个块非常方便。每个块中的最后一条CSV记录可能会被截断,因此需要进行某种检查,并且&#34;片段&#34;必须保存该块并将其附加到下一个块以重建记录。

plupload方法的优点是可以使MySQL导入显然占用零时间 - 一旦上传完成,即使文件非常大,数据也可用。你把它放在一个带有临时名称的表中(不是一个MySQL临时表,否则你会冒数据丢失的风险),当上传完成后你只需要自动重命名表。