it-roy-ru.com

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

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

14
chimbu

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

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

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

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

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

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

  6. Глядя на ваши образцы строк ИМХО вам не нужен 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)

Теперь ИМХО вам нужно преобразовать довольно много полей при их загрузке:

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

    Неправильное значение даты и времени: «18.09.2013 01:53:45 PM» для столбца «дата» в строке 

  2. вы должны иметь дело с одиночными кавычками вокруг значений в поле 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> ЗАГРУЗИТЬ ДАННЫЕ INFILE '/tmp/detection.csv'
 -> В ТАБЛИЦУ Calldetections 
 -> ПОЛЯ, ПРЕКРАЩЕННЫЕ ',' 
 -> ДОПОЛНИТЕЛЬНО ЗАКРЫТЫМ '' '
 -> ЛИНИИ, ПРЕКРАЩЕННЫЕ' ,\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 (ОБА'\'' FROM @number), 
 -> duration = 1 * TRIM (ТРЕЙЛИНГ 'Secs FROM @duration), 
 -> addr = NULLIF (@addr, 'null'), 
 -> pin = NULLIF (@pin, 'null'), 
 -> city = NULLIF (@city, 'null'), 
 -> состояние = NULLIF (@state, 'null'), 
 -> страна = NULLIF (@country, 'null'); 
 Запрос в порядке, затронуто 3 строки (0,00 с) 
 Записей: 3 Удалено: 0 Пропущено: 0 Предупреждений: 0 

 Mysql> select * from calldetections; 
 + --------------------- + ----- ---- + --------------- + ------------- + ---------- + ---- - + ------ + ------ + ------- + --------- + ------ + ------ +
 | дата | имя | тип | номер | продолжительность | адрес | пин-код | город | штат | страна | лат | журнал |. .__ + --------------------- + --------- + ------------- - + ------------- + ---------- + ------ + ------ + ------ + - ------ + --------- + ------ + ------ + 
 | 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') ";
40
peterm

Вставить в базу данных более 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, но мы рассчитываем, что и после того, как вставить это.

0
krunal panchal