加快csv导入速度



我想导入大量的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

有人能帮我用fastercsv提高速度吗?

我认为它不会变得更快。

也就是说,一些测试表明,很大一部分时间花在了代码转换上(对于我的测试用例,大约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

更新

您也可以尝试将解析拆分为几个线程。我在试验这个代码时达到了一些性能提升(处理遗漏的标题):

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中所有块的结果都是联合的,仅此而已。

顾名思义,更快的CSV是更快的:)

http://fastercsv.rubyforge.org

另请参阅。了解更多信息

Ruby on Rails从CSV到FasterCSV

我很好奇这个文件有多大,有多少列。

使用CSV.foreach是首选方式。在应用程序运行时查看内存配置文件会很有趣。(有时速度慢是因为打印,所以请确保你不会做得比你需要的更多)

您可以对其进行预处理,并排除任何没有esupp的行,因为您的代码似乎只关心这些行。此外,您可以截断任何不关心的右侧列。

另一种技术是收集唯一的组件并将它们放入散列中。似乎您多次触发同一个查询。

你只需要描述一下它,看看它在哪里度过。

查看Gem smarter_csv!它可以分块读取CSV文件,然后您可以创建Resque作业来进一步处理这些块并将其插入数据库。

https://github.com/tilo/smarter_csv

最新更新