Сохранение нескольких объектов за один звонок в рельсах - PullRequest
75 голосов
/ 24 марта 2010

У меня есть метод в рельсах, который делает что-то вроде этого:

a = Foo.new("bar")
a.save

b = Foo.new("baz")
b.save

...
x = Foo.new("123", :parent_id => a.id)
x.save

...
z = Foo.new("zxy", :parent_id => b.id)
z.save

Проблема в том, что чем дольше я добавляю, тем дольше и дольше. Я подозреваю, что это потому, что он должен попасть в базу данных для каждой записи. Поскольку они вложенные, я знаю, что не могу спасти детей до того, как спасутся родители, но я бы хотел спасти всех родителей сразу, а затем всех детей. Было бы неплохо сделать что-то вроде:

a = Foo.new("bar")
b = Foo.new("baz")
...
saveall(a,b,...)

x = Foo.new("123", :parent_id => a.id)
...
z = Foo.new("zxy", :parent_id => b.id)
saveall(x,...,z)

Это сделало бы все за два попадания в базу данных. Есть ли простой способ сделать это в рельсах, или я застрял, делая это по одному?

Ответы [ 5 ]

83 голосов
/ 24 марта 2010

Так как вам нужно выполнить несколько вставок, база данных будет поражена несколько раз. Задержка в вашем случае заключается в том, что каждое сохранение выполняется в разных транзакциях БД. Вы можете уменьшить задержку, объединив все свои операции в одну транзакцию.

class Foo
  belongs_to  :parent,   :class_name => "Foo"
  has_many    :children, :class_name => "Foo", :foreign_key=> "parent_id"
end

Ваш метод сохранения может выглядеть так:

# build the parent and the children
a = Foo.new(:name => "bar")
a.children.build(:name => "123")

b = Foo.new("baz")
b.children.build(:name => "zxy")

#save parents and their children in one transaction
Foo.transaction do
  a.save!
  b.save!
end

Вызов save родительского объекта сохраняет дочерние объекты.

62 голосов
/ 24 марта 2010

Вы можете попробовать использовать Foo.create вместо Foo.new. Создать «Создает объект (или несколько объектов) и сохраняет его в базе данных, если проверки пройдены. Полученный объект возвращается независимо от того, был ли объект успешно сохранен в базе данных или нет».

Вы можете создать несколько объектов, таких как:

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

Затем для каждого родителя вы также можете использовать create, чтобы добавить к его ассоциации:

parents.each do |parent|
  parent.children.create (:child_name => 'abc')
end

Я рекомендую прочитать как документацию ActiveRecord , так и руководства Rails по Интерфейсу запросов ActiveRecord и Ассоциации ActiveRecord . Последний содержит руководство по всем методам, которые класс получает, когда вы объявляете ассоциацию.

9 голосов
/ 12 марта 2012

Один из двух ответов, найденных где-то еще: Берлингтон . Эти два - ваш лучший выбор для производительности


Я думаю, что лучшим выбором для производительности будет использование SQL и массовая вставка нескольких строк в запросе. Если вы можете построить оператор INSERT, который делает что-то вроде:

INSERT INTO foos_bars (foo_id, bar_id) ЗНАЧЕНИЯ (1,1), (1,2), (1,3) .... Вы должны быть в состоянии вставить тысячи строк в одном запросе. Я не пробовал ваш метод mass_habtm, но кажется, что вы могли бы что-то вроде:


bars = Bar.find_all_by_some_attribute(:a) 
foo = Foo.create
values = bars.map {|bar| "(#{foo.id},#{bar.id})"}.join(",") 
connection.execute("INSERT INTO foos_bars (foo_id, bar_id) VALUES
#{values}")

Кроме того, если вы ищете Bar по параметру some_attribute, убедитесь, что это поле проиндексировано в вашей базе данных.


OR

Вы все еще можете взглянуть на activerecord-import. Это верно, что это не работает без модели, но вы можете создать модель только для импорта.


FooBar.import [:foo_id, :bar_id], [[1,2], [1,3]]

Приветствия

0 голосов
/ 25 января 2019

вам нужно использовать этот драгоценный камень "FastInserter" -> https://github.com/joinhandshake/fast_inserter

и вставка большого количества и тысяч записей выполняется быстро, потому что этот драгоценный камень пропускает активную запись и использует только один запрос sql raw

0 голосов
/ 21 августа 2014

Вам не нужен драгоценный камень, чтобы поразить БД быстро и только один раз!

Джекрг разработал это для нас: https://gist.github.com/jackrg/76ade1724bd816292e4e

...