Ruby on Rails-CSV 파일에서 데이터 가져 오기


205

CSV 파일의 데이터를 기존 데이터베이스 테이블로 가져오고 싶습니다. CSV 파일을 저장하고 싶지 않고 데이터를 가져 와서 기존 테이블에 넣습니다. Ruby 1.9.2와 Rails 3을 사용하고 있습니다.

이것은 내 테이블입니다.

create_table "mouldings", :force => true do |t|
  t.string   "suppliers_code"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "name"
  t.integer  "supplier_id"
  t.decimal  "length",         :precision => 3, :scale => 2
  t.decimal  "cost",           :precision => 4, :scale => 2
  t.integer  "width"
  t.integer  "depth"
end

이 작업을 수행하는 가장 좋은 방법을 보여주는 코드를 제공해 주시겠습니까? 감사합니다.

답변:


380
require 'csv'    

csv_text = File.read('...')
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
  Moulding.create!(row.to_hash)
end

2
당신은 .... 어디서든 좋아하는 레이크 작업에 넣어 또는 컨트롤러 액션에, 또는 수
yfeldblum

1
완벽하게 작동했습니다. 그러나 초보자 수준의 질문이 있습니다. Ruby 및 Rails API 설명서에서 설명 된 메소드를 탐색하려고 할 때 해당 메소드를 찾을 수 없었습니다 (공식 Ruby 및 Rails 사이트, API 문서를 보았습니다). 예를 들어 어떤 객체가 CSV.parse ()를 반환하는지 찾을 수 없었고, to_hash () 및 with_indifferent_access () 메소드를 찾지 못했습니다 ... 어쩌면 내가 잘못된 위치를 보았거나 Ruby & Rails API를 통과하는 방법에 대한 기본 원칙을 놓쳤습니다. 문서. 누구든지 Ruby API 문서를 읽는 방법에 대한 모범 사례를 공유 할 수 있습니까?
Vladimir Kroz

2
@ daveatflow : 예, 아래의 대답을 참조하십시오. 파일에서 한 번에 한 줄씩 읽습니다.
Tom De Leu

1
@ lokeshjain2008은 OP의 모델을 나타냅니다.
Justin D.

3
이 방법은 비효율적입니다! 거대한 CSV 파일에서 램 사용량이 급증합니다. 아래의 것이 좋습니다.
unom

206

yfeldblum의 대답의 간단한 버전. 더 간단하고 큰 파일에서도 잘 작동합니다.

require 'csv'    

CSV.foreach(filename, :headers => true) do |row|
  Moulding.create!(row.to_hash)
end

with_indifferent_access 또는 symbolize_keys가 필요 없으며 파일에서 먼저 문자열을 읽을 필요가 없습니다.

전체 파일을 한 번에 메모리에 보관하지 않지만 한 줄씩 읽고 한 줄에 몰딩을 만듭니다.


1
큰 파일 크기를 관리하는 것이 더 낫습니까? 한 번에 한 줄씩 읽습니까?
NotSimon

1
@ 시몬 : 참으로. 전체 파일을 한 번에 메모리에 보관하지 않지만 한 줄씩 읽고 한 줄에 몰딩을 만듭니다.
Tom De Leu

이 오류가 발생합니다. 이유를 아십니까? : ActiveModel :: UnknownAttributeError : 알 수없는 속성 '사이렌; nom_ent; 주소; 보완; 주소; cp_ville; 지불; 지역; 부서; 활동; 날짜; nb_salaries; nom; prenom; civilite; adr_mail; libele_acti ; categorie; tel '거래를위한
nico_lrx

1
@AlphaNico 문제가있는 질문을 작성하십시오. 이 오류는 이것과 관련이 없으며 Model 객체가 동기화되지 않은 것 같습니다.
unom

이 경우 어떻게 TestCases를 작성합니까?
Afolabi Olaoluwa Akinwumi

11

smarter_csv보석은 특히이 사용 사례 만들었습니다 : CSV 파일에서 데이터를 읽고 신속하게 데이터베이스 항목을 만들 수 있습니다.

  require 'smarter_csv'
  options = {}
  SmarterCSV.process('input_file.csv', options) do |chunk|
    chunk.each do |data_hash|
      Moulding.create!( data_hash )
    end
  end

이 옵션 chunk_size을 사용하여 한 번에 N csv 행을 읽은 다음 내부 루프에서 Resque를 사용하여 새 레코드를 즉시 작성하지 않고 새 레코드를 작성하는 작업을 생성 할 수 있습니다.이 방법으로 항목 생성로드를 분산시킬 수 있습니다 여러 근로자에게.

참조 : https://github.com/tilo/smarter_csv


3
CSV 클래스가 포함되어 있으므로 보석을 추가하거나 설치하는 대신 사용하는 것이 좋습니다. 물론, 새로운 보석을 응용 프로그램에 추가 할 것을 제안하지 않았습니다. 특정 목적을 위해 일련의 개별 보석을 추가하는 것이 매우 쉽고 응용 프로그램에 과도한 종속성이 있음을 알기 전에. (의도적으로 보석을 추가하는 것을 피하고 있습니다. 매장에서는 팀원에게 추가를 정당화해야합니다.)
Tass

1
@Tass 또한 특정 목적을 위해 일련의 개별 메소드를 추가하는 것이 매우 쉽고, 알기 전에 애플리케이션에 유지해야 할 과도한 논리가 있습니다. 보석이 작동하고, 잘 관리되고, 적은 자원을 사용하거나 관련 환경 (예 : 생산 작업을위한 준비)에 격리 될 수있는 경우 항상 보석을 사용하는 것이 더 나은 옵션 인 것 같습니다 . 루비와 레일즈는 코드를 적게 쓰는 것에 관한 것입니다.
zrisher

다음과 같은 오류가 있습니다. 이유를 알고 있습니까? ActiveModel :: UnknownAttributeError : 알 수없는 속성 '사이렌; nom_ent; 주소; complement_adresse; cp_ville; 지불; 지역; 부서; 활동; 날짜; nb_salaries; nom; prenom; civilite; adr_mail; libele_acti; categorie;
tel'for

나는 이것을 레이크 작업으로 시도했지만 콘솔은 다음을 반환합니다. 레이크가 중단되었습니다! NoMethodError : 정의되지 않은 메서드 전무에 대한`가까운 'NilClass stackoverflow.com/questions/42515043/...
마르코스 R. 게바라

1
@Tass는 CSV 처리를 청킹하고 속도를 높이고 메모리를 절약하는 것이 새로운 보석을 추가하는 데 정당화 될 수 있습니다.)
Tilo

5

시도해 볼 수 있습니다 Upsert:

require 'upsert' # add this to your Gemfile
require 'csv'    

u = Upsert.new Moulding.connection, Moulding.table_name
CSV.foreach(file, headers: true) do |row|
  selector = { name: row['name'] } # this treats "name" as the primary key and prevents the creation of duplicates by name
  setter = row.to_hash
  u.row selector, setter
end

이것이 원하는 경우, 테이블에서 자동 증가 기본 키를 제거하고 기본 키를로 설정하는 것도 고려할 수 있습니다 name. 또는 기본 키를 구성하는 속성 조합이 있으면 선택기로 사용하십시오. 색인이 필요하지 않으며 더 빨라집니다.



2

데이터베이스 관련 프로세스를 transaction블록 안에 넣는 것이 좋습니다 . 코드 스 니펫 블로우는 언어 모델에 언어 세트를 시드하는 전체 프로세스입니다.

require 'csv'

namespace :lan do
  desc 'Seed initial languages data with language & code'
  task init_data: :environment do
    puts '>>> Initializing Languages Data Table'
    ActiveRecord::Base.transaction do
      csv_path = File.expand_path('languages.csv', File.dirname(__FILE__))
      csv_str = File.read(csv_path)
      csv = CSV.new(csv_str).to_a
      csv.each do |lan_set|
        lan_code = lan_set[0]
        lan_str = lan_set[1]
        Language.create!(language: lan_str, code: lan_code)
        print '.'
      end
    end
    puts ''
    puts '>>> Languages Database Table Initialization Completed'
  end
end

아래 스 니펫은 languages.csv파일 의 일부입니다 .

aa,Afar
ab,Abkhazian
af,Afrikaans
ak,Akan
am,Amharic
ar,Arabic
as,Assamese
ay,Aymara
az,Azerbaijani
ba,Bashkir
...


0

더 좋은 방법은 레이크 작업에 포함시키는 것입니다. / lib / tasks / 안에 import.rake 파일을 만들고이 코드를 해당 파일에 넣습니다.

desc "Imports a CSV file into an ActiveRecord table"
task :csv_model_import, [:filename, :model] => [:environment] do |task,args|
  lines = File.new(args[:filename], "r:ISO-8859-1").readlines
  header = lines.shift.strip
  keys = header.split(',')
  lines.each do |line|
    values = line.strip.split(',')
    attributes = Hash[keys.zip values]
    Module.const_get(args[:model]).create(attributes)
  end
end

그 후 터미널 에서이 명령을 실행하십시오. rake csv_model_import[file.csv,Name_of_the_Model]


0

나는 그것이 오래된 질문이라는 것을 알고 있지만 여전히 구글의 첫 10 링크에 있습니다.

루프에서 데이터베이스 호출을 유발하고 특히 많은 양의 데이터를 삽입해야 할 때 행을 하나씩 저장하는 것은 매우 효율적이지 않습니다.

배치 인서트를 사용하는 것이 좋습니다.

INSERT INTO `mouldings` (suppliers_code, name, cost)
VALUES
    ('s1', 'supplier1', 1.111), 
    ('s2', 'supplier2', '2.222')

이러한 쿼리를 수동으로 작성 Model.connection.execute(RAW SQL STRING)(권장 하지 않음)하거나 gem activerecord-import(2010 년 8 월 11 일에 처음 릴리스 된 것 )을 사용하는 것보다 데이터를 배열에 rows넣고 호출 할 수 있습니다.Model.import rows

자세한 내용은 gem 문서를 참조하십시오


-2

CSV :: Table을 사용하고을 사용하는 것이 좋습니다 String.encode(universal_newline: true). CRLF와 CR을 LF로 변환


1
제안 된 솔루션은 무엇입니까?
Tass

-3

SmartCSV를 사용하려는 경우

all_data = SmarterCSV.process(
             params[:file].tempfile, 
             { 
               :col_sep => "\t", 
               :row_sep => "\n" 
             }
           )

이것은 "\t"행을 새 줄로 구분하여 각 행에서 탭으로 구분 된 데이터를 나타냅니다."\n"

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.