在Rails中创建大规模HABTM关联的最快方法是什么?

我有两个表,在Rails中有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 = Foo.create @bars = Bar.find_all_by_some_attribute(:a) 

最快的方法是什么? 我试过了:

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

两者都运行得很慢,每个bar都有如下条目:

bars_foos列(1.1ms)显示来自bars_foos SQL(0.6ms)INSERT INTO bars_foosbar_idfoo_id )VALUES( foo_id

我查看了ar-extensions,但是如果没有一个模型(Model.import)排除了它用于连接表, import函数似乎不起作用。

我需要编写SQL,还是Rails有更漂亮的方式?

我认为性能方面最好的选择是使用SQL,并为每个查询批量插入多行。 如果您可以构建一个类似于以下内容的INSERT语句:

 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”搜索Bar,请确保在数据库中将该字段编入索引。

您仍然可以查看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 

这比等效的本机rails代码要快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 

在我看来,这是一个直截了当的操作,它应该在Rails中得到有效支持,我很想听听是否有人有更清洁的方式。

我正在运行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 

此外,您应该尝试在生产中运行相同的内容,并查看您获得的性能,因为生产中的许多缓存仍然在开发中不一定发生。