Ответ 1
require 'csv'
csv_text = File.read('...')
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
Moulding.create!(row.to_hash)
end
Я хотел бы импортировать данные из файла 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
Можете ли вы дать мне код, чтобы показать мне лучший способ сделать это, спасибо.
require 'csv'
csv_text = File.read('...')
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
Moulding.create!(row.to_hash)
end
Упрощенная версия ответа yfeldblum, что проще и хорошо работает с большими файлами:
require 'csv'
CSV.foreach(filename, :headers => true) do |row|
Moulding.create!(row.to_hash)
end
Нет необходимости в with_indifferent_access или symbolize_keys, и не нужно сначала читать файл в строке.
Он не сохраняет весь файл в памяти сразу, но читается в строковой последовательности и создает литье в строке.
Камень 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
Это может помочь. Он также имеет примеры кода:
http://csv-mapper.rubyforge.org/
Или для выполнения команды rake для этого:
Вы можете попробовать 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
. В качестве альтернативы, если есть некоторая комбинация атрибутов, которые образуют первичный ключ, используйте это как селектор. Индекс не нужен, он просто ускоряется.
Лучше обернуть процесс, связанный с базой данных, внутри блока 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
...
Используйте этот драгоценный камень: https://rubygems.org/gems/active_record_importer
class Moulding < ActiveRecord::Base
acts_as_importable
end
Затем вы можете использовать:
Moulding.import!(file: File.open(PATH_TO_FILE))
Просто убедитесь, что ваши заголовки соответствуют именам столбцов вашей таблицы
Лучше всего включить его в граблированную задачу. Создайте файл import.rake внутри/lib/tasks/и поместите этот код в этот файл.
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]
Я знаю этот старый вопрос, но он все еще находится в первых 10 ссылках в google.
Невозможно сохранить строки один за другим, потому что вызывает вызов базы данных в цикле, и вам лучше избежать этого, особенно если вам нужно вставить огромные части данных.
Лучше (и значительно быстрее) использовать пакетную вставку.
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
(он был выпущен 11 августа 2010 года), в этом случае просто поместите данные в массив rows
и вызовите Model.import rows
Лучше использовать CSV:: Table и использовать String.encode(universal_newline: true)
. Он преобразует CRLF и CR в LF
Если вы хотите использовать SmartCSV
all_data = SmarterCSV.process(
params[:file].tempfile,
{
:col_sep => "\t",
:row_sep => "\n"
}
)
Это представляет данные с разделителями табуляции в каждой строке "\t"
с строками, разделенными новыми строками "\n"