web-dev-qa-db-ja.com

PHP-LOAD DATA INFILEを使用してCSVファイルをmysqlデータベースにインポートする

私はそのような.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); 

しかし、何も挿入されません。間違いはどこですか?

14
chimbu

実行する前にecho($sql);を実行すると、次の理由でクエリの構文が正しくないことがわかります。

  1. ファイル名は、識別子ではなく文字列リテラルであるため、バッククォートではなく引用符で囲む必要があります。

  2. _FIELDS TERMINATED BY_および_ENCLOSED BY_および_ESCAPED BY_句で区切り文字を指定するためにmysql_escape_string()を呼び出す必要はまったくありません。

  3. バックティックを使いすぎます。実際、あなたの場合、予約語は使用されていないため、すべてを捨てます。煩雑になるだけです。

  4. CSVファイルの最初の行の最後に持っている必要があります _,,,_を使用しているのは、これらを行区切り文字の一部として使用しているためです。そうしない場合は、最初の行だけでなく、データを含む2番目の行もスキップします。

  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. テーブル内のdatedatetimeデータ型の場合、変換する必要があります。そうでない場合はエラーが発生します

    誤った日時値:行の列 'date'の 'Sep-18-2013 01:53:45 PM'

  2. Numberフィールドの値を囲む単一のqoutesを処理する必要があります

  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 
-> FIELDS TERMINATED BY'、 '
-> OPTIONALLY ENCLOSED BY '"' 
-> LINES TERMINATED BY ',\n' 
-> 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'); 
クエリOK、影響を受ける3行(0.00秒)
レコード:3削除:0スキップ:0警告:0 
 
 mysql> select * from calldetections; 
 + --------------------- + --------- + --------------- +- ------------ + ---------- + ------ + ------ + ------ + ----- -+ --------- + ------ + ------ + 
 |日付|名前|タイプ|番号|期間| addr |ピン|市|状態|国| lat |ログ| 
 + --------------------- + --------- + ---------- ----- + ------------- + ---------- + ------ + ------ + ----- -+ ------- + --------- + ------ + ------ + 
 | 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') ";
_
41
peterm

データベースに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にはありませんが、それを計算して挿入した後です。

0
krunal panchal