PHP - 使用LOAD DATA INFILE将CSV文件导入mysql数据库

时间:2013-09-20 10:58:52

标签: php mysql sql csv load-data-infile

我有像这样的.csv文件数据

Date,Name,Call Type,Number,Duration,Address,PostalCode,City,State,Country,Latitude,Longitude
"Sep-18-2013 01:53:45 PM","Unknown","outgoing call",'123456',"0 Secs","null","null","null","null","null",0.0,0.0,,,
"Sep-18-2013 01:54:14 PM","Unknown","outgoing call",'1234567890',"0 Secs","null","null","null","null","null",0.0,0.0,,,
"Sep-18-2013 01:54:37 PM","Unknown","outgoing call",'14772580369',"1 Secs","null","null","null","null","null",0.0,0.0,,,

我正在使用以下代码将数据插入数据库

$sql = "LOAD DATA INFILE `detection.csv`
              INTO TABLE `calldetections`
              FIELDS TERMINATED BY '".@mysql_escape_string(",").
             "` OPTIONALLY ENCLOSED BY `".@mysql_escape_string("\"").
             "` OPTIONALLY ENCLOSED BY `".@mysql_escape_string("\'").
             "` ESCAPED BY `".@mysql_escape_string("\\").
              "` LINES TERMINATED BY `".",,,\\r\\n".
             "`IGNORE 1 LINES `"

             ."(`date`,`name`,`type`,`number`,`duration`,`addr`,`pin`,`city`,`state`,`country`,`lat`,`log`)";
      $res = @mysql_query($con,$sql); 

但没有插入;错误在哪里?

2 个答案:

答案 0 :(得分:41)

如果您在执行之前执行echo($sql);,则会发现您的查询语法不正确,原因如下:

  1. 文件名应该用引号括起来,而不是反引号,因为它是字符串文字而不是标识符。

  2. 完全无需致电mysql_escape_string()FIELDS TERMINATED BY以及ENCLOSED BYESCAPED BY条款中指定分隔符。

  3. 你过度使用反叛。事实上,在你的情况下,由于没有使用保留字,你就抛弃了它们。它们只会增加混乱。

  4. 在CSV文件的第一行末尾,您必须 ,,,,因为您将它们用作行分隔符的一部分。如果你不这样做,你不仅会跳过第一行,还会跳过包含数据的第二行。

  5. 您不能多次使用ENCLOSED BY子句。您必须以不同的方式处理Number字段。

  6. 查看您的示例行恕我直言,您不需要ESCAPED BY。但如果您觉得需要,可以像ESCAPED BY '\\'一样使用它。

  7. 这就是说语法正确的陈述可能看起来像这样

    LOAD DATA INFILE 'detection.csv'
    INTO TABLE calldetections
    FIELDS TERMINATED BY ','
    OPTIONALLY ENCLOSED BY '"' 
    LINES TERMINATED BY ',,,\r\n'
    IGNORE 1 LINES 
    (date, name, type, number, duration, addr, pin, city, state, country, lat, log)
    

    现在恕我直言,你需要在加载它们时转换很多字段:

    1. 如果您的表中的date属于datetime数据类型,则需要进行转换,否则您将收到错误

        

      日期时间值不正确:'Sep-18-2013 01:53:45 PM'列'日期'在行

    2. 您必须处理Number字段

    3. 中值的单个qoutes
    4. 您很可能希望将"null"字符串文字更改为NULL列的实际addr, pin, city, state, country

    5. 如果持续时间总是以秒为单位,那么您可以提取秒的整数值并将其存储在表格中,以便以后能够轻松汇总持续时间值。

    6. 据说这个陈述的有用版本看起来应该是这样的

      LOAD DATA INFILE 'detection.csv'
      INTO TABLE calldetections
      FIELDS TERMINATED BY ','
      OPTIONALLY ENCLOSED BY '"' 
      LINES TERMINATED BY ',,,\r\n'
      IGNORE 1 LINES 
      (@date, name, type, @number, @duration, @addr, @pin, @city, @state, @country, lat, log)
      SET date = STR_TO_DATE(@date, '%b-%d-%Y %h:%i:%s %p'),
          number = TRIM(BOTH '\'' FROM @number),
          duration = 1 * TRIM(TRAILING 'Secs' FROM @duration),
          addr = NULLIF(@addr, 'null'),
          pin  = NULLIF(@pin, 'null'),
          city = NULLIF(@city, 'null'),
          state = NULLIF(@state, 'null'),
          country = NULLIF(@country, 'null') 
      

      以下是在我的机器上执行查询的结果

      mysql> LOAD DATA INFILE '/tmp/detection.csv'
          -> INTO TABLE calldetections
          -> FIELDS TERMINATED BY ','
          -> OPTIONALLY ENCLOSED BY '"' 
          -> LINES TERMINATED BY ',,,\n'
          -> IGNORE 1 LINES 
          -> (@date, name, type, @number, @duration, @addr, @pin, @city, @state, @country, lat, log)
          -> SET date = STR_TO_DATE(@date, '%b-%d-%Y %h:%i:%s %p'),
          ->     number = TRIM(BOTH '\'' FROM @number),
          ->     duration = 1 * TRIM(TRAILING 'Secs' FROM @duration),
          ->     addr = NULLIF(@addr, 'null'),
          ->     pin  = NULLIF(@pin, 'null'),
          ->     city = NULLIF(@city, 'null'),
          ->     state = NULLIF(@state, 'null'),
          ->     country = NULLIF(@country, 'null');
      Query OK, 3 rows affected (0.00 sec)
      Records: 3  Deleted: 0  Skipped: 0  Warnings: 0
      
      mysql> select * from calldetections;
      +---------------------+---------+---------------+-------------+----------+------+------+------+-------+---------+------+------+
      | date                | name    | type          | number      | duration | addr | pin  | city | state | country | lat  | log  |
      +---------------------+---------+---------------+-------------+----------+------+------+------+-------+---------+------+------+
      | 2013-09-18 13:53:45 | Unknown | outgoing call | 123456      |        0 | NULL | NULL | NULL | NULL  | NULL    | 0.0  | 0.0  |
      | 2013-09-18 13:54:14 | Unknown | outgoing call | 1234567890  |        0 | NULL | NULL | NULL | NULL  | NULL    | 0.0  | 0.0  |
      | 2013-09-18 13:54:37 | Unknown | outgoing call | 14772580369 |        1 | NULL | NULL | NULL | NULL  | NULL    | 0.0  | 0.0  |
      +---------------------+---------+---------------+-------------+----------+------+------+------+-------+---------+------+------+
      3 rows in set (0.00 sec)
      

      最后在php中将查询字符串分配给$sql变量应该看起来像这样

      $sql = "LOAD DATA INFILE 'detection.csv'
              INTO TABLE calldetections
              FIELDS TERMINATED BY ','
              OPTIONALLY ENCLOSED BY '\"' 
              LINES TERMINATED BY ',,,\\r\\n'
              IGNORE 1 LINES 
              (@date, name, type, @number, @duration, @addr, @pin, @city, @state, @country, lat, log)
              SET date = STR_TO_DATE(@date, '%b-%d-%Y %h:%i:%s %p'),
                  number = TRIM(BOTH '\'' FROM @number),
                  duration = 1 * TRIM(TRAILING 'Secs' FROM @duration),
                  addr = NULLIF(@addr, 'null'),
                  pin  = NULLIF(@pin, 'null'),
                  city = NULLIF(@city, 'null'),
                  state = NULLIF(@state, 'null'),
                  country = NULLIF(@country, 'null') ";
      

答案 1 :(得分:0)

在数据库中以1分钟为单位插入超过7000000条记录(计算超高速查询)

    mysqli_query($cons, '
    LOAD DATA LOCAL INFILE "'.$file.'"
    INTO TABLE tablename
    FIELDS TERMINATED by \',\'
    LINES TERMINATED BY \'\n\'
    IGNORE 1 LINES
    (isbn10,isbn13,price,discount,free_stock,report,report_date)
     SET RRP = IF(discount = 0.00,price-price * 45/100,IF(discount = 0.01,price,IF(discount != 0.00,price-price * discount/100,@RRP))),
         RRP_nl = RRP * 1.44 + 8,
         RRP_bl = RRP * 1.44 + 8,
         ID = NULL
    ')or die(mysqli_error());
    $affected = (int) (mysqli_affected_rows($cons))-1; 
    $log->lwrite('Inventory.CSV to database:'. $affected.' record inserted successfully.');

RRP和RRP_nl以及RRP_bl不在csv中,但是在插入之后我们计算出来。