我想将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
你能给我一些代码,告诉我最好的方法吗,谢谢。
答案 0 :(得分:358)
require 'csv'
csv_text = File.read('...')
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
Moulding.create!(row.to_hash)
end
答案 1 :(得分:187)
更简单的yfeldblum版本的答案,更简单,也适用于大文件:
require 'csv'
CSV.foreach(filename, :headers => true) do |row|
Moulding.create!(row.to_hash)
end
不需要with_indifferent_access或symbolize_keys,也不需要先将文件读入字符串。
它不会立即将整个文件保存在内存中,而是逐行读取并在每行创建一个成型。
答案 2 :(得分:10)
专门为此用例创建了smarter_csv
gem:从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-rows,然后在内部循环中使用Resque生成将创建新记录的作业,而不是立即创建它们 - 这样您可以将生成条目的负载分散到多个工作人员。
答案 3 :(得分:4)
答案 4 :(得分:4)
您可以尝试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
。或者,如果存在形成主键的某些属性组合,请将其用作选择器。没有索引是必要的,只会让它更快。
答案 5 :(得分:1)
最好将数据库相关进程包装在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
...
答案 6 :(得分:0)
使用此gem: https://rubygems.org/gems/active_record_importer
class Moulding < ActiveRecord::Base
acts_as_importable
end
然后你现在可以使用:
Moulding.import!(file: File.open(PATH_TO_FILE))
请确保您的标题与表格的列名相匹配
答案 7 :(得分:0)
更好的方法是将其包含在rake任务中。在/ 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]
答案 8 :(得分:0)
我知道它的旧问题,但它仍然在Google的前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
答案 9 :(得分:0)
以下模块可以扩展到任何模型,它将根据 CSV 中定义的列标题导入数据。
注意:
在您的 "csv_importer.rb"
文件夹中创建一个名为 models/concerns
的文件
module CsvImporter
extend ActiveSupport::Concern
require 'csv'
def convert_csv_to_book_attributes(csv_path)
csv_rows = CSV.open(csv_path).each.to_a.compact
columns = csv_rows[0].map(&:strip).map(&:to_sym)
csv_rows.shift
return columns, csv_rows
end
def import_by_csv(csv_path)
columns, attributes_array = convert_csv_to_book_attributes(csv_path)
message = ""
begin
self.import columns, attributes_array, validate: false
message = "Import Successful."
rescue => e
message = e.message
end
return message
end
end
将 extend CsvImporter
添加到您希望将此功能扩展到的任何模型。
在您的控制器中,您可以通过如下操作来利用此功能:
def import_file
model_name = params[:table_name].singularize.camelize.constantize
csv = params[:file].path
@message = model_name.import_by_csv(csv)
end
答案 10 :(得分:-2)
最好使用CSV :: Table并使用String.encode(universal_newline: true)
。它将CRLF和CR转换为LF
答案 11 :(得分:-3)
如果您想使用SmartCSV
all_data = SmarterCSV.process(
params[:file].tempfile,
{
:col_sep => "\t",
:row_sep => "\n"
}
)
这表示每行"\t"
中的制表符分隔数据,行以新行"\n"
分隔