Какой самый быстрый способ создать массовые ассоциации HABTM в рельсах?

StackOverflow https://stackoverflow.com/questions/2203420

Вопрос

У меня есть две таблицы, с отношениями HABTM в рельсах. Что -то вроде следующего:

class Foo < ActiveRecord::Base
  has_and_belongs_to_many :bars
end

class Bar < ActiveRecord::Base
  has_and_belongs_to_many :foos
end

Теперь у меня есть новый Foo объект, и хочу массово-провести тысячи баров, которые я предварительно загрузил:

@foo = Foo.create
@bars = Bar.find_all_by_some_attribute(:a)

Какой самый быстрый способ сделать это? Я пытался:

@foo.bars = @bars
@foo.bars << @bars

И оба бегают очень медленно, с записью, подобной следующей для каждого bar:

столбцы bars_foos (1,1 мс) Показать поля из bars_foos SQL (0,6 мс) вставьте в bars_foos (bar_id, foo_id) Значения (100, 117200)

Я посмотрел на артистику, но import Функция, кажется, не работает без модели (model.import), которая исключает его использование для таблицы соединений.

Нужно ли написать SQL или у рельс более красивый способ?

Это было полезно?

Решение

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

INSERT INTO foos_bars (foo_id,bar_id) VALUES (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}")

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

Другие советы

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

class FooBar < ActiveRecord::Base; end

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

Вы можете обернуть это в транзакцию, чтобы гарантировать, что HABTM полностью заполняется, как и здесь:

ActiveRecord::Base.transaction do
  imported_foo = Foo.import( foo_names, foo_values )
  imported_bar = Bar.import( bar_names, bar_values )
  FooBar.import( [:foo_id, :bar_id], imported_foo.ids.zip(imported_bar.ids)
end

Это было быстрее, чем эквивалентный код местных рельсов в течение 7:

class << Foo
  def mass_habtm(attr_array)
    attr_str = attr_array.map{|a| %Q{'#{a}'} }.uniq.join(",")
    self.connection.execute(%Q{insert into foos_bars (foo_id,bar_id) 
                     select distinct foos.id,bars.id from foos,bars 
                     where foos.id = #{self.id} 
                     and bars.some_attribute in (#{attr_str})})
  end
end

Мне кажется, что это достаточно простая операция, которую она должна быть эффективно поддержана в рельсах, я хотел бы услышать, если у кого -то есть более чистый путь.

Я бегу 2.2.2, Может быть, он реализован более эффективно в 3.x? и нашел то же самое на 3.0.2.

Честно, has_and_belongs_to_many это очень устаревший способ ведения дел. Вы, вероятно, должны изучить has_many :through, который является новым способом выполнения таблиц, и уже давно.

class Foo < ActiveRecord::Base
  has_many :foobars
  has_many :bars, :through => :foobars

  def add_many_bars(bars)
    bars.each do |bar|
      self.bars << bar
    end
  end
end

class Bar < ActiveRecord::Base
  has_many :foobars
  has_many :foos, :through => :foobars
end

class FooBar < ActiveRecord::Base
  belongs_to :foo
  belongs_to :bar
end

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top