2012-08-28 25 views
5

Quiero importar gran cantidad de datos cvs (no directamente a AR, pero después de algunas recuperaciones), y mi código es muy lento.Acelerar la importación de 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 

Incluso si borro y obtengo solo esto, es lento.

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 

¿Alguien me puede ayudar a aumentar la velocidad con fastercsv?

+0

Esto no afectará a la velocidad, pero no está cerrando la 'file' Uso File.readlines ("/ archivo"). Entonces no tiene que preocuparse por dejar un archivo abierto. –

+0

@TheWho no te entiendo, ¿podría dar ejemplo de detalle? – byCoder

+0

Si ejecuta File.open, debe cerrar el archivo. No quiere filtrar archivos abiertos. http://stackoverflow.com/questions/4795447/rubys-file-open-and-the-need-for-f-close –

Respuesta

2

Yo no creo que vaya a ser mucho más rápido.

Dicho esto, algunas pruebas muestran que se dedica una gran parte del tiempo a la transcodificación (aproximadamente 15% para mi caso de prueba). Entonces, si pudieras omitir eso (por ejemplo, al crear el CSV en UTF-8 ya), verías una mejora.

Además, de acuerdo con ruby-doc.org la interfaz "primario" para CSVs lectura es foreach, por lo que este debe ser preferido:

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 

actualización

También podría intentar dividir el análisis en varias trapos. Llegué a un cierto aumento de rendimiento experimentar con el código (tratamiento de la partida excluido):

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 

Esto divide la entrada en trozos de 10000 líneas y crea un hilo de análisis para cada uno de los trozos. Cada subproceso se transfiere a una subarreglo en la matriz parts para almacenar su resultado. Cuando se finalizan todos los hilos (después de threads.each(&:join)), los resultados de todos los fragmentos en parts son conjuntos y eso es todo.

+0

hm, ¿podrías reescribir tu actualización de acuerdo con mi pregunta? ... podrías obtener + 50 – byCoder

+0

también, ¿cómo puedo convertir mi csv a utf8 para que Ruby lo entienda? Intento utf8, todo está bien, pero cuando en mi utf8-doc aparece la palabra en ruso (tendrá muchas de ellas) envía utf8 errror ... ¿cómo lo resuelvo? – byCoder

+0

Lo siento, no entiendo esa pregunta. ¿Tiene Ruby problemas para leer el CSV UTF-8 o tiene el CSV una codificación diferente? Tal vez deberías publicar otra pregunta sobre Stackoverflow. –

2

Como su nombre implica más rápido CSV es más rápido :) Bueno

http://fastercsv.rubyforge.org

ver también. de algo más de información

Ruby on Rails Moving from CSV to FasterCSV

+0

es standart ruby ​​1.9 fastercsv !!! ¡Algunos admin @sawa borraron de mi pregunta esta importante palabra! – byCoder

+0

@PavelBY Acabas de tener esa palabra entre paréntesis, aislada de otras senencias, y no estaba claro lo que significaba. – sawa

+0

@sawa por favor haz lo que hice antes! pero haz esto como dices => claro y sensato – byCoder

0

Tengo curiosidad por el tamaño del archivo y la cantidad de columnas que tiene.

Usar CSV.foreach es la forma preferida. Sería interesante ver el perfil de la memoria mientras se ejecuta su aplicación. (A veces la lentitud se debe a la impresión, así que asegúrese de no hacer más de lo que necesita)

Es posible que pueda preprocesarlo y excluir cualquier fila que no tenga el esupp, ya que parece que tu código solo se preocupa por esas filas. Además, puedes truncar cualquier columna del lado derecho que no te importe.

Otra técnica sería reunir los componentes únicos y ponerlos en un hash. Parece que estás disparando la misma consulta varias veces.

Solo necesita crear un perfil y ver dónde está gastando su tiempo.

+0

esa consulta a través de los hallazgos toma tiempo, lo sé, pero ¿por qué esta csv se está abriendo durante tanto tiempo ... como variante que codifica, pero cómo lo resuelve? También abrí para esta pregunta, pero nadie ayuda – byCoder

+0

bien, es difícil de ayudar si no conocemos los detalles importantes. Elimine los datos confidenciales y las partes importantes en una esencia que podamos verificar. Hazlo más fácil para ayudarte y obtendrás ayuda, estoy seguro de eso. –

0

echa un vistazo a la gema smarter_csv! Puede leer archivos CSV en fragmentos, y luego puede crear trabajos Resque para procesar e insertar esos fragmentos en una base de datos.

https://github.com/tilo/smarter_csv

Cuestiones relacionadas