J'aimerais importer des données d'un fichier CSV dans une table de base de données existante. Je ne veux pas enregistrer le fichier CSV, mais en extraire les données et les placer dans la table existante. J'utilise Ruby 1.9.2 et Rails 3.
Ceci est ma table:
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
Pouvez-vous me donner du code pour me montrer le meilleur moyen de le faire, merci.
require 'csv'
csv_text = File.read('...')
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
Moulding.create!(row.to_hash)
end
La version simplifiée de la réponse de yfeldblum est plus simple et convient également aux fichiers volumineux:
require 'csv'
CSV.foreach(filename, :headers => true) do |row|
Moulding.create!(row.to_hash)
end
Pas besoin de with_indifferent_access ni de symbolize_keys, et pas besoin de lire le fichier dans une chaîne en premier.
Il ne garde pas tout le fichier en mémoire en une fois, mais lit ligne par ligne et crée un moulage par ligne.
La gemme smarter_csv
a été créée spécifiquement pour ce cas d'utilisation: lire des données à partir d'un fichier CSV et créer rapidement des entrées de base de données.
require 'smarter_csv'
options = {}
SmarterCSV.process('input_file.csv', options) do |chunk|
chunk.each do |data_hash|
Moulding.create!( data_hash )
end
end
Vous pouvez utiliser l'option chunk_size
pour lire simultanément N lignes-csv, puis utiliser Resque dans la boucle interne pour générer des travaux qui créeront les nouveaux enregistrements, plutôt que de les créer tout de suite. Vous pourrez ainsi répartir la génération entrées à plusieurs travailleurs.
Voir également: https://github.com/tilo/smarter_csv
Vous pouvez essayer 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
Si vous le souhaitez, vous pouvez également vous débarrasser de la clé primaire à incrémentation automatique et définir la clé primaire sur name
. Sinon, s'il existe une combinaison d'attributs formant une clé primaire, utilisez-la comme sélecteur. Aucun index n'est nécessaire, cela ne fera que l'accélérer.
Cela peut aider. Il a aussi des exemples de code:
http://csv-mapper.rubyforge.org/
Ou pour une tâche de rake pour faire la même chose:
Il est préférable de placer le processus lié à la base de données dans un bloc transaction
. Le coup de code est un processus complet d’ensemencement d’un ensemble de langages au modèle Langage,
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
Le fragment ci-dessous est une partie du fichier languages.csv
,
aa,Afar
ab,Abkhazian
af,Afrikaans
ak,Akan
am,Amharic
ar,Arabic
as,Assamese
ay,Aymara
az,Azerbaijani
ba,Bashkir
...
Je sais que c'est une vieille question, mais cela reste dans les 10 premiers liens de Google.
Il n'est pas très efficace de sauvegarder les lignes une par une, car cela provoque un appel de la base de données et vous évitez de le faire, en particulier lorsque vous devez insérer de grandes quantités de données.
Il est préférable (et nettement plus rapide) d'utiliser l'insertion par lot.
INSERT INTO `mouldings` (suppliers_code, name, cost)
VALUES
('s1', 'supplier1', 1.111),
('s2', 'supplier2', '2.222')
Vous pouvez créer une telle requête manuellement et ensuite utiliser Model.connection.execute(RAW SQL STRING)
(non recommandé) Ou utiliser gem activerecord-import
(la première publication a eu lieu le 11 août 2010). Dans ce cas, il suffit de placer les données dans le tableau rows
et d'appeler Model.import rows
Le meilleur moyen consiste à l'inclure dans une tâche de commission. Créez le fichier import.rake dans/lib/tasks/et mettez ce code dans ce fichier.
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
Après cela, lancez cette commande dans votre terminal rake csv_model_import[file.csv,Name_of_the_Model]
Utilisez cette gemme: https://rubygems.org/gems/active_record_importer
class Moulding < ActiveRecord::Base
acts_as_importable
end
Ensuite, vous pouvez maintenant utiliser:
Moulding.import!(file: File.open(PATH_TO_FILE))
Assurez-vous simplement que vos en-têtes correspondent aux noms de colonne de votre table.