2013-08-07 44 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值,一次加载它们,并在插入之前重新映射它们。

相关问题