2013-08-07 2 views
1

나는 아래와 같은 GES 파일에 코딩 기록이 데이터를 데이터베이스로 가져 오려면 어떻게해야합니까?

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

수천 개의 데이터베이스가 :

  • 00F는 열 머리글
  • 00I 수단이

이 행을 삽입 의미 다른 사람도 있습니다 (행을 삭제하려면 00D 또는). 업데이트 용)

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 열과 관련된 테이블에 넣어야하므로 전체 프로세스가 복잡해질 수 있습니다. 지금이 데이터를 가져 오려면 몇 시간이 걸리기 때문입니다.

아마도 Ruby를 사용하는 것이 최선의 방법이 아닙니다.

+0

프로세스 중 가장 느린 부분을 살펴 보았습니까? 아마 일괄 삽입 SQL 및 여러 작은 버전으로 파일을 분할하고 병렬로 그들을 실행할 수 있습니까? – HariKrishnan

+0

가장 느린 것은 group_id를 찾을 때마다입니다. 나는 그것을 병렬로하고 싶지만 어쩌면 내가 어떻게 나눠야 하는지를 보여줄 수있다. 복사하여 붙여 넣기하여 파일을 수동으로 잘라낼 수 있습니까? – tomekfranek

+0

group_id 조회에 가장 많은 시간이 걸리는 경우 그룹 ID를 redis와 같은 키 값 저장소에 덤프하여 O (1) 조회를 제공 할 수 있습니다. 그것은 일을 더 빨리 만들 수 있습니다.또한 동일한 루프에 삽입하는 대신 동일한 크기의 파일로 입력을 분할하고 각 레코드의 SQL 삽입을 파서로 파서를 통해 실행할 수 있습니다. 그런 다음 insert 문을 대량으로 실행할 수 있습니다. 업데이트 또는 삭제 작업이 있습니까? – HariKrishnan

답변

2

여기 Ruby에서 처리 할 수있는 것은 없습니다. "함수 프로그래밍"이 어떻게 도움이되는지는 명확하지 않습니다. 이것은 단순한 데이터 변환과 함께 고전적인 상태 - 기계 종류의 문제이기 때문에 마찬가지입니다.

예 발판이 같은 코드를 작성할 때

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

중간 결과에 노출해야합니다 :

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 

데이터에 따라이의 예 출력은 같다 무슨 일이 일어나고 있는지 테스트 할 수 있습니다. 구현시 데이터를 한 번에 데이터베이스에서 직접 덤프하므로 제대로 작동하지 않는 경우 문제가 발생한 곳을 알기가 매우 어렵습니다. 이 버전은 여러 가지 방법으로 구성되어 있으며 각 메소드는보다 구체적인 용도로 사용됩니다.

원본 예제에서 group_id을 전혀 해결하지 못하는 이유는 명확하지 않지만 샘플 출력은 그와 아무 관련이 없습니다. 예를 들어 필자는이를 해결하고 캐싱을 유지하는 방법을 포함 시켰습니다. 같은 일의 조회. 대규모 가져 오기의 경우 많은 행을로드하고 고유 한 group_id 값을 추출한 다음 한 번에 모두로드하고 삽입하기 전에 다시 매핑 할 수 있습니다.

관련 문제