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

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

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

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

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

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

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

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

  6. Глядя на ваши образцы строк 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 вам нужно преобразовать немало полей, пока вы их загружаете:

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

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

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

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

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

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

 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
     -> ПОЛИГРАФИИ, ПРЕРЫВАННЫЕ ','
     -> ДОПОЛНИТЕЛЬНО ЗАВЕРШЕНО '' ' 
     -> LINES TERMINATED BY ',,, \ n'
     -> IGNORE 1 ЛИНИИ 
     -> (@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');
 Запрос ОК, 3 строки затронуты (0.00 сек)
 Записей: 3 Удалено: 0 Пропущено: 0 Предупреждения: 0

 mysql> select * from calldetections;
 + --------------------- + --------- + --------------- + - ------------ + ---------- + ------ + ------ + ------ + ----- - + --------- + ------ + ------ +
 |  дата |  имя |  тип |  номер |  продолжительность |  addr |  pin |  город |  состояние |  страна |  lat |  log |
 + --------------------- + --------- + --------------- + - ------------ + ---------- + ------ + ------ + ------ + ----- - + --------- + ------ + ------ +
 |  2013-09-18 13:53:45 |  Неизвестно |  исходящий звонок |  123456 |  0 |  NULL |  NULL |  NULL |  NULL |  NULL |  0.0 |  0.0 |
 |  2013-09-18 13:54:14 |  Неизвестно |  исходящий звонок |  1234567890 |  0 |  NULL |  NULL |  NULL |  NULL |  NULL |  0.0 |  0.0 |
 |  2013-09-18 13:54:37 |  Неизвестно |  исходящий звонок |  14772580369 |  1 |  NULL |  NULL |  NULL |  NULL |  NULL |  0.0 |  0.0 |
 + --------------------- + --------- + --------------- + - ------------ + ---------- + ------ + ------ + ------ + ----- - + --------- + ------ + ------ +
 3 ряда в наборе (0,00 сек)

И, наконец, в 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') "; 

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