2013-08-07 42 views
1

我有上千條記錄我應該如何將這些數據導入到我的數據庫中?

Code | Name | Price 
00106 | Water | 9.99 
00107 | Onion | 8.99 

這是GES文件編碼方式類似於下面的數據庫:

  • 00F意味着列標題
  • 00I手段插入一行

有也有其他人喜歡(00D刪除行或00U更新)

00F 
0101 
02Code 
031 
00F 
0102 
02Name 
031 
00F 
0103 
02Price 
030 
00I 
0100106 
02Water 
030999 
00I 
0100107 
02Onion 
030899 

我想創建進口商處理這個文件,並將其推入我的數據庫。所以我開始實施:

class Importer 
    CONN = ActiveRecord::Base.connection 
    F = "00F" 
    I = "00I" 

    def extract_to_database(collection) 
    add  = true 
    tmp  = [] 
    type  = F 
    inserts = [] 

    collection.each_with_index do |line, i| 
     _type = line.strip 
     _changed = [F,I].include? _type 

     if _changed && i > 0 
     case type 
     when F then @f << tmp 
     when I 
      group_id = Group.find_by(code: tmp[1]).id 
      inserts.push "(group_id,'#{tmp[2]}','#{tmp[3]}')" 
     end 

     tmp = [] 
     type = _type 
     end 

     tmp << line 
    end 
    sql = "INSERT INTO products (`group_id`, `name`, `price`) VALUES #{inserts.join(", ")}" 
    CONN.execute sql 
    end 
end 

有一個問題,我想重構,使用函數式編程。

而且我將不得不通過code找到其他型號,並將其與products表相關聯some_model_id列,因此這可能會使整個過程變得複雜。因爲現在導入這些數據需要幾個小時。

也許使用Ruby不是最好的選擇。

+0

你有沒有看看過程中最慢的部分。也許批量插入sql並將文件拆分爲多個較小的版本並且並行運行它們可以提供幫助嗎? – HariKrishnan

+0

最慢的是每次查找group_id。我想同時做到這一點,但也許你可以告訴我該怎麼分開。通過複製粘貼手動剪切文件? – tomekfranek

+0

如果group_id查找所花費的時間最多,也許可以將組標識轉儲到鍵值存儲,如redis,從而實現O(1)查找。這可能會讓事情變得更快。而不是在同一個循環中插入,也許你可以將輸入分成相同大小的文件並通過解析器運行它們,該解析器爲每條記錄分配sql插入。然後你可以批量運行插入語句。是否還有更新或刪除操作? – HariKrishnan

回答

2

這裏沒有什麼Ruby不能處理的。目前還不清楚「函數式編程」是如何幫助這一點的,因爲這是一個經典的狀態機問題,有一些簡單的數據轉換正在進行。

實施例的支架:

class SomethingImporter 
    FIELD_MARKER = "00F" 
    INSERT_MARKER = "00I" 

    COLUMNS = %w[ group_id name price ] 

    # Performs the insert into a given model. This should probably be a class 
    # method on the model itself. 
    def bulk_insert(model, rows) 
    sql = [ 
     "INSERT INTO `#{model.table_name}` (#{columns.collect { |c| }}" 
    ] 

    # Append the placeholders: (?,?,?),(?,?,?),... 
    sql[0] += ([ '(%s)' % ([ '?' ] * COLUMNS.length).join(',') ] * rows.length).join(',') 

    sql += rows.flatten 

    model.connection.execute(model.send(:sanitize_sql, sql)) 
    end 

    # Resolve a group code to a group_id value, and cache the result so that 
    # subsequent look-ups for the same code are valid. 
    def group_id(group_code) 
    @find_group ||= { } 

    # This tests if any value has been cached for this code, including one 
    # that might be nil. 
    if (@find_group.key?(group_code)) 
     return @find_group[group_code] 
    end 

    group = Group.find_by(code: group_code) 

    @find_group[group_code] = group && group.id 
    end 

    # Call this with the actual collection, lines stripped, and with any header 
    # lines removed (e.g. collection.shift) 
    def extract_rows(collection) 
    state = nil 
    rows = [ ] 
    row = [ ] 

    collection.each_with_index do |line| 
     case (line) 
     when FIELD_MARKER 
     # Indicates field data to follow 
     state = :field 
     when INSERT_MARKER 
     case (state) 
     when :insert 
      rows << [ row[0], row[1], (row[2].sub(/^0+/, '').to_f/100) ] 
     end 

     state = :insert 
     row = [ ] 
     else 
     case (state) 
     when :field 
      # Presumably you'd pay attention to the data here and establish 
      # a mapping table. 
     when :insert 
      row << line.sub(/^\d\d/, '') 
      # puts row.inspect 
     end 
     end 
    end 

    case (state) 
    when :insert 
     rows << [ row[0], row[1], (row[2].sub(/^0+/, '').to_f/100) ] 
    end 

    rows 
    end 
end 


data = <<END 
00F 
0101 
02Code 
031 
00F 
0102 
02Name 
031 
00F 
0103 
02Price 
030 
00I 
0100106 
02Water 
030999 
00I 
0100107 
02Onion 
030899 
END 

importer = SomethingImporter.new 

puts importer.extract_rows(data.split(/\n/)).inspect 

從這個輸出的例子中,基於數據,看起來像:

[["00106", "Water", 9.99], ["00107", "Onion", 8.99]] 

當寫這樣的代碼,一定要暴露中間結果在爲了能夠測試發生了什麼。您的實現需要一次性將數據直接轉儲到數據庫中,如果出現問題,很難判斷出現問題的地方。這個版本由幾種方法組成,每種方法都有一個更具體的目的。

在您的原始示例中並不清楚爲什麼您完全解決了group_id,您的示例輸出與此無關,但作爲一個示例,我包含了解決這些問題並使其緩存的方法,避免重複查找同樣的東西。對於更大規模的導入,您可能會加載多行,提取不同的group_id值,一次加載它們,並在插入之前重新映射它們。

相關問題