Подтвердить что ты не робот

PHP - импортировать CSV файл в базу данных mysql. Используя 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 '"[email protected]_escape_string(",").
             "` OPTIONALLY ENCLOSED BY `"[email protected]_escape_string("\"").
             "` OPTIONALLY ENCLOSED BY `"[email protected]_escape_string("\'").
             "` ESCAPED BY `"[email protected]_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); 

но ничего не вставлено; где ошибка?

4b9b3361

Ответ 1

Если вы сделаете echo($sql); перед его выполнением, вы увидите, что синтаксис вашего запроса неверен по следующим причинам:

  • Имя файла должно быть заключено в кавычки, а не обратные, потому что это строковый литерал, а не идентификатор.

  • Абсолютно нет необходимости вызывать mysql_escape_string() для указания разделителя в FIELDS TERMINATED BY и ENCLOSED BY и ESCAPED BY предложениях.

  • Вы злоупотребляете обратными окнами. На самом деле, в вашем случае, поскольку нет зарезервированных слов, вы бросаете их все. Они только добавляют беспорядок.

  • В конце самой первой строки вашего CSV файла вы должны иметь ,,,, потому что используете их как часть разделителя строк. Если вы этого не сделаете, вы пропустите не только первую строку, но и вторую, содержащую данные.

  • Вы не можете использовать предложение ENCLOSED BY более одного раза. Вы должны иметь дело с полем Number по-другому.

  • Глядя на ваши строки строк IMHO, вам не нужно ESCAPED BY. Но если вам кажется, что вам это нужно, используйте его так ESCAPED BY '\\'.

При этом синтаксически корректный оператор может выглядеть так:

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)

Теперь IMHO вам нужно преобразовать несколько полей при их загрузке:

  • если date в вашей таблице имеет тип данных datetime, тогда его нужно преобразовать, иначе вы получите сообщение об ошибке

    Неверное значение даты и времени: 'Sep-18-2013 01:53:45 PM' для столбца 'date' в строке

  • вам нужно иметь дело с одиночными qoutes вокруг значений в поле Number

  • вы, скорее всего, захотите изменить "null" строковый литерал на фактический NULL для addr, pin, city, state, country столбцов

  • Если длительность всегда находится в секундах, вы можете извлечь целочисленное значение секунд и сохранить ее таким образом в своей таблице, чтобы позднее можно было агрегировать значения продолжительности.

При этом полезная версия инструкции должна выглядеть примерно так:

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') ";

Ответ 2

Вставить объем более 7000000 записей за 1 минуту в базе данных (сверхбыстрый запрос с расчетом)

    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, но мы вычисляем, что и после вставки, что.