5

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

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

fastcsv を使用して速度を上げるのを手伝ってくれる人はいますか?

4

4 に答える 4

2

その名前が示すように、Faster CSV の方がはるかに高速です :)

http://fastercsv.rubyforge.org

も参照してください。詳細については

CSV から FasterCSV への Ruby on Rails の移行

于 2012-08-28T20:17:31.567 に答える
2

あまり速くならないと思います。

とはいえ、一部のテストでは、かなりの時間がトランスコーディングに費やされていることが示されています (私のテスト ケースでは約 15%)。したがって、それをスキップできれば (たとえば、既に CSV を UTF-8 で作成するなど)、改善が見られるでしょう。

また、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

アップデート

解析を複数のスレッドに分割することもできます。このコードを試してみると、パフォーマンスがいくらか向上しました(見出しの扱いは省略されています):

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

これにより、入力が 10000 行のチャンクに分割され、チャンクごとに解析スレッドが作成されます。parts各スレッドは、その結果を格納するために配列内のサブ配列に渡されます。すべてのスレッドが終了すると( の後threads.each(&:join))、すべてのチャンクの結果partsが結合され、それだけです。

于 2012-09-11T13:00:09.553 に答える
0

ファイルの大きさと列数が気になります。

CSV.foreach を使用することをお勧めします。アプリの実行中にメモリ プロファイルを確認すると興味深いでしょう。(印刷が原因で速度が低下する場合があるため、必要以上に印刷を行わないように注意してください)

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

もう 1 つの手法は、一意のコンポーネントを集めてハッシュに入れることです。同じクエリを複数回実行しているようです。

プロファイリングして、どこで時間を費やしているかを確認するだけです。

于 2012-09-13T17:08:25.037 に答える
0

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

https://github.com/tilo/smarter_csv

于 2013-04-13T18:33:06.643 に答える