2012-08-28 13 views
5

大量のcvsデータを(ARに直接ではなく、いくつかのフェッチ後に)インポートしたいのですが、私のコードは非常に遅いです。スピードアップcsvインポート

def csv_import 
    require 'csv' 
    file = File.open("/#{Rails.public_path}/uploads/shate.csv") 
    csv = CSV.open(file, "r:ISO-8859-15:UTF-8", {:col_sep => ";", :row_sep => :auto, :headers => :first_row}) 

    csv.each do |row| 
     #ename,esupp= row[1].split(/_/) 
     #(ename,esupp,foo) = row[1]..split('_') 
     abrakadabra = row[0].to_s() 
     (ename,esupp) = abrakadabra.split(/_/) 
     eprice = row[6] 
     eqnt = row[1] 
     # logger.info("1) ") 
     # logger.info(ename) 
     # logger.info("---") 
     # logger.info(esupp) 
     #---- 
     #ename = row[4] 
     #eprice = row[7] 
     #eqnt = row[10] 
     #esupp = row[12] 

     if ename.present? && ename.size>3 
     search_condition = "*" + ename.upcase + "*"  

     if esupp.present? 
      #supplier = @suppliers.find{|item| item['SUP_BRAND'] =~ Regexp.new(".*#{esupp}.*") } 
      supplier = Supplier.where("SUP_BRAND like ?", "%#{esupp}%").first 
      logger.warn("!!! *** supp !!!") 
      #logger.warn(supplier) 
     end 

     if supplier.present? 

      @search = ArtLookup.find(:all, :conditions => ['MATCH (ARL_SEARCH_NUMBER) AGAINST(? IN BOOLEAN MODE)', search_condition.gsub(/[^0-9A-Za-z]/, '')]) 
      @articles = Article.find(:all, :conditions => { :ART_ID => @search.map(&:ARL_ART_ID)}) 
      @art_concret = @articles.find_all{|item| item.ART_ARTICLE_NR.gsub(/[^0-9A-Za-z]/, '').include?(ename.gsub(/[^0-9A-Za-z]/, '')) } 

      @aa = @art_concret.find{|item| item['ART_SUP_ID']==supplier.SUP_ID} #| @articles 
      if @aa.present? 
      @art = Article.find_by_ART_ID(@aa) 
      end 

      if @art.present? 
      @art.PRICEM = eprice 
      @art.QUANTITYM = eqnt 
      @art.datetime_of_update = DateTime.now 
      @art.save 
      end 

     end 
     logger.warn("------------------------------")  
     end 

     #logger.warn(esupp) 
    end 
end 

これを削除して取得しても、それは遅いです。

def csv_import 
    require 'csv' 
    file = File.open("/#{Rails.public_path}/uploads/shate.csv") 
    csv = CSV.open(file, "r:ISO-8859-15:UTF-8", {:col_sep => ";", :row_sep => :auto, :headers => :first_row}) 

    csv.each do |row| 
    end 
end 

誰でも私がfastercsvを使用してスピードを上げるのに役立つでしょうか?

+0

これは、スピードには影響しませんが、あなたは、 'file'使用File.readlines( "/ファイル")を閉鎖されていません。その後、ファイルを開いたままにする心配はありません。 –

+0

@あなたを理解していない、具体例を与えることができますか? – byCoder

+0

File.openを実行する場合は、ファイルを閉じる必要があります。開いているファイルをリークさせたくありません。 http://stackoverflow.com/questions/4795447/rubys-file-open-and-the-need-for-f-close –

答えて

2

のために私はそれをはるかに高速になりますとは思いません。つまり、一部のテストでは、かなりの時間がトランスコードに費やされていることがわかりました(テストケースでは約15%)。そのため、スキップすることができれば(たとえば、UTF-8でCSVを作成しているなど)、改善が見られます。

また、ruby-doc.orgに応じてCSVを読み取るための「プライマリ」インターフェースが foreachあるので、これが好まれる必要があります。

def csv_import 
    import 'csv' 
    CSV.foreach("/#{Rails.public_path}/uploads/shate.csv", {:encoding => 'ISO-8859-15:UTF-8', :col_sep => ';', :row_sep => :auto, :headers => :first_row}) do | row | 
    # use row here... 
    end 
end 

更新

また、いくつかの中にパースを分割しようとすることができスレッド。これは、10000個の行のチャンクへの入力を分割し、チャンクのそれぞれのための構文解析スレッドを作成

N = 10000 
def csv_import 
    all_lines = File.read("/#{Rails.public_path}/uploads/shate.csv").lines 
    # parts will contain the parsed CSV data of the different chunks/slices 
    # threads will contain the threads 
    parts, threads = [], [] 
    # iterate over chunks/slices of N lines of the CSV file 
    all_lines.each_slice(N) do | plines | 
    # add an array object for the current chunk to parts 
    parts << result = [] 
    # create a thread for parsing the current chunk, hand it over the chunk 
    # and the current parts sub-array 
    threads << Thread.new(plines.join, result) do | tsrc, tresult | 
     # parse the chunk 
     parsed = CSV.parse(tsrc, {:encoding => 'ISO-8859-15:UTF-8', :col_sep => ";", :row_sep => :auto}) 
     # add the parsed data to the parts sub-array 
     tresult.replace(parsed.to_a) 
    end 
    end 
    # wait for all threads to finish 
    threads.each(&:join) 
    # merge all the parts sub-arrays into one big array and iterate over it 
    parts.flatten(1).each do | row | 
    # use row (Array) 
    end 
end 

:私はこのコードを試していくつかのパフォーマンスの増加(取り残さ見出しの治療)に達しました。各スレッドは配列partsのサブアレイにその結果を格納するために渡されます。すべてのスレッドが完了すると(threads.each(&:join)の後)、すべてのチャンクの結果はpartsになります。

+0

hm、私の質問にあなたの更新を書き換えることができますか? ...あなたも+50 – byCoder

+0

を得ることができます、どのように私はルビーがそれを理解するようにutf8に私のCSVを変換できますか?私はutf8を試してみる、すべては大丈夫だが、私のutf8-docにはロシア語が表示されている(それは多くのものがある)utf8 errrorを送信する。 – byCoder

+0

申し訳ありませんが、私はその質問を理解していません。 RubyはUTF-8 CSVの読み込みに問題があるのですか?またはCSVに別のエンコーディングがありますか?たぶん別のStackoverflow質問を投稿する必要があります。 –

2

それは名前がありますように:)より速くCSVが速くまあある意味も

http://fastercsv.rubyforge.org

参照してください。いくつかの詳細情報

Ruby on Rails Moving from CSV to FasterCSV

+0

も参照してください。それはstandartルビー1.9のfastercsvです!いくつかのadmin @澤は私の質問からこの重要な言葉を削除しました! – byCoder

+0

@PavelBYあなたはちょうどそのカッコ内に他のセンテンスから隔離された単語がありました。 – sawa

+0

@sawa前と同じようにしてください!あなたが言うようにこれを行う=>明確で分かりやすい – byCoder

0

私は、ファイルの大きさとその列の数が不思議です。

CSV.foreachを使用するのが好ましい方法です。あなたのアプリが動いているときのメモリプロファイルを見るのは面白いでしょう。 (印刷速度が遅いことがありますので、必要以上に印刷しないようにしてください)

前処理してesuppを持たない行を除外することは可能かもしれませんあなたのコードがそれらの行だけを気にするように見えます。また、気にしない右側の列を切り捨てることもできます。

別のテクニックは、固有のコンポーネントを集めてハッシュに入れることです。同じクエリを複数回実行しているようです。

あなたはそれをプロファイルし、時間をどこに費やしているかを確認するだけです。

+0

結果を介してのクエリは私が知っている時間がかかるが、なぜこのCSVは長い間開いている...変種私はエンコーディングだが、どのようにそれを解決する?また、私はこの質問のために開いたが、誰も助けてくれない – byCoder

+0

私たちが重要な細部を知らなければ助けてくれるのは難しい。重要なデータを抜き出し、重要な部分を確認してチェックアウトすることができます。助けをより簡単にすると、助けを得ることになるでしょう、私はそれを確信しています。 –

0

gem smarter_csvをチェックしてください!チャンク内のCSVファイルを読み取ることができます。その後、Resqueジョブを作成して、これらのチャンクをさらに処理してデータベースに挿入することができます。

https://github.com/tilo/smarter_csv

関連する問題