2012-08-28 2 views
5

Voglio importare una grande quantità di dati cvs (non direttamente in AR, ma dopo alcuni recuperi), e il mio codice è molto lento.Accelera importazione 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 

Anche se elimini e ottengo solo questo, è 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 

Qualcuno può aiutarmi ad aumentare la velocità utilizzando quickcsv?

+0

Ciò non influirà la velocità, ma non si è chiudere il 'file' Usa file.readlines ("/ file"). Quindi non devi preoccuparti di lasciare un file aperto. –

+0

@ Chi non ti capisce, potrebbe dare un esempio di dettaglio? – byCoder

+0

Se si esegue File.open, è necessario chiudere il file. Non vuoi perdere file aperti. http://stackoverflow.com/questions/4795447/rubys-file-open-and-the-need-for-f-close –

risposta

2

Io non credo che sarà molto più veloce.

Detto questo, alcuni test mostrano che una parte significativa del tempo viene spesa per la transcodifica (circa il 15% per il mio caso di test). Quindi, se puoi saltarlo (ad esempio creando già il CSV in UTF-8) vedresti dei miglioramenti.

Inoltre, secondo ruby-doc.org l'interfaccia "primario" per CSV lettura è foreach, quindi questo dovrebbe essere preferito:

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 

Aggiornamento

Si potrebbe anche provare a dividere l'analisi in più filettature. Ho raggiunto un certo aumento delle prestazioni sperimentazione di questo codice (trattamento della rubrica omesso):

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 

Questo divide l'ingresso in blocchi di 10000 linee e crea un thread analisi per ciascuno dei blocchi. Ogni thread viene consegnato sotto una matrice secondaria nell'array parts per la memorizzazione del risultato. Quando tutti i thread sono finiti (dopo threads.each(&:join)) i risultati di tutti i blocchi in parts sono uniti e il gioco è fatto.

+0

hm, potresti riscrivere il tuo aggiornamento accodando alla mia domanda? ... potresti ottenere + 50 – byCoder

+0

anche, come posso convertire il mio csv in utf8 in modo che Ruby lo capisca? provo utf8, tutto è ok, ma quando nel mio utf8-doc appare la parola russa (ne avrà molti di loro) invia utf8 errror ... come risolverlo? – byCoder

+0

Scusa, non capisco quella domanda. Ruby ha problemi a leggere il file CSV UTF-8 o ha una codifica diversa dal CSV? Forse dovresti pubblicare un'altra domanda StackOverflow. –

2

Come il nome implica più veloce CSV è ben più veloce :)

http://fastercsv.rubyforge.org

anche vedere. per un po 'di informazioni

Ruby on Rails Moving from CSV to FasterCSV

+0

è standard rubino 1.9 fastercsv !!! Alcuni admin @sawa hanno cancellato dalla mia domanda questa parola importante !!! – byCoder

+0

@PavelBY Hai appena avuto questa parola tra parentesi, isolata da altre senanze, e non era chiaro cosa significasse. – sawa

+0

@sawa si prega di fare come ho fatto prima! ma fai ciò come dici => chiaro e sensato – byCoder

0

Sono curioso di sapere quanto è grande il file e quante colonne ha.

L'utilizzo di CSV.foreach è il metodo preferito. Sarebbe interessante vedere il profilo della memoria mentre la tua app è in esecuzione. (A volte la lentezza è dovuta alla stampa, quindi assicurati di non fare più di quello che ti serve)

Potrebbe essere in grado di pre-elaborarlo ed escludere qualsiasi riga che non ha l'esupp, poiché sembra che il tuo codice si preoccupi solo di quelle file. Inoltre, puoi troncare le colonne di destra che non ti interessano.

Un'altra tecnica sarebbe quella di raccogliere i componenti unici e metterli in un hash. Sembra che tu stia licenziando la stessa query più volte.

Hai solo bisogno di profilo e vedere dove sta spendendo il suo tempo.

+0

che la ricerca tramite i risultati richiede tempo so, ma perché questo csv si apre così a lungo ... come variante che codifica, ma come risolverlo? anche io ho aperto questa domanda, ma nessuno aiuta – byCoder

+0

bene, è difficile aiutare se non conosciamo i dettagli importanti. Spoglia i dati sensibili e attraverso le parti importanti in un senso che potremmo verificare. Renditi più facile aiutarti e avrai un po 'd'aiuto, ne sono sicuro. –

0

controlla la gemma smarter_csv! È in grado di leggere i file CSV in blocchi e quindi è possibile creare processi Resque per elaborare ulteriormente e inserire tali blocchi in un database.

https://github.com/tilo/smarter_csv