web-dev-qa-db-fra.com

Insertion par lots dans Rails 3

Je souhaite effectuer une insertion par lots de quelques milliers d'enregistrements dans la base de données (POSTGRES dans mon cas) depuis mon application Rails.

Quelle serait la "manière de Rails" de le faire? Quelque chose qui est rapide et aussi correct.

Je sais que je peux créer la requête SQL par concaténation de chaînes d'attributs, mais je souhaite une meilleure approche.

36
phoenixwizard

ActiveRecord .create method prend en charge la création en bloc. La méthode émule la fonctionnalité si la base de données ne la prend pas en charge et utilise le moteur de base de données sous-jacent si la fonctionnalité est prise en charge.

Il suffit de passer un tableau d'options.

# Create an Array of new objects
User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])

Le blocage est pris en charge et constitue le moyen commun d'attributs partagés.

# Creating an Array of new objects using a block, where the block is executed for each object:
User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
  u.is_admin = false
end
55
Simone Carletti

J'ai finalement trouvé une solution après les deux réponses de Simone Carletti et Sumit Munot.

Jusqu'à ce que le pilote postgres prenne en charge l'insertion en bloc de la méthode ActiveRecord .create, j'aimerais utiliser activerecord-import gem . Il insère en vrac et cela aussi dans une seule instruction d'insertion.

books = []
10.times do |i| 
    books << Book.new(:name => "book #{i}")
end
Book.import books

Dans POSTGRES, il en résulte un seul état d'insertion.

Une fois que le pilote postgres a pris en charge l'insertion en bloc de la méthode ActiveRecord .create dans une seule instruction insert, la solution de @Simone Carletti est plus logique :)

20
phoenixwizard

Vous pouvez créer un script dans votre modèle Rails, écrire vos requêtes à insérer dans ce scriptIn Rails, vous pouvez exécuter le script à l'aide de

Rails runner MyModelName.my_method_name

Est-ce la meilleure façon que j'ai utilisée dans mon projet.

Mettre à jour:

J'utilise le suivant dans mon projet mais cela ne convient pas pour l'injection SQL . Si vous n'utilisez pas la saisie de l'utilisateur dans cette requête, cela peut fonctionner pour vous

user_string = " ('[email protected]','a'), ('[email protected]','b')"
User.connection.insert("INSERT INTO users (email, name) VALUES"+user_string)

Pour plusieurs enregistrements:  

new_records = [
  {:column => 'value', :column2 => 'value'}, 
  {:column => 'value', :column2 => 'value'}
]

MyModel.create(new_records)
2
Sumit Munot

Vous pouvez le faire de manière rapide ou à l’aide de Rails;) Le meilleur moyen, selon mon expérience, d’importer des données en vrac vers Postgres est via CSV. Ce qui prendra plusieurs minutes à la manière de Rails prendra plusieurs secondes en utilisant la capacité d'importation CSV native de Postgres.

http://www.postgresql.org/docs/9.2/static/sql-copy.html

Il déclenche même des déclencheurs de base de données et respecte leurs contraintes.

Edit (après votre commentaire): Gotcha. Dans ce cas, vous avez correctement décrit vos deux options. Je suis dans la même situation auparavant, je l'ai implémenté avec la sauvegarde Rails 1000! stratégie parce que c’est la méthode la plus simple qui a fonctionné, puis optimisée selon la stratégie «Ajouter une chaîne de requête très longue», car elle était plus performante du point de vue de la magnitude.

Bien entendu, l’optimisation prématurée est la racine de tous les maux. Vous pouvez donc le faire de la manière simple et lente de Rails et savoir que la construction d’une grande chaîne de requête est une technique parfaitement légitime d’optimisation au détriment de la maintenabilité. Je pense que votre vraie question est la suivante: existe-t-il une méthode Railsy qui ne comporte pas des milliers de requêtes? - Malheureusement, la réponse à cette question est non.

0
Chris Aitchison