我有两个表,在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.bars = @bars
@foo.bars << @bars
两者都运行得非常慢,每个bar
都有如下条目:
bars_foos列(1.1ms)显示 FIELDS FROM
bars_foos
SQL(0.6ms) INSERT INTObars_foos
(bar_id
,foo_id
)VALUES(100,117200)
我查看了ar-extensions,但import
函数似乎没有模型(Model.import),它排除了它用于连接表。
我是否需要编写SQL,或者Rails是否有更漂亮的方式?
答案 0 :(得分:7)
我认为在性能方面最好的选择是使用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,请确保在数据库中将该字段编入索引。
答案 1 :(得分:6)
您仍然可以查看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
答案 2 :(得分:1)
这比等效的本机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上发现相同。
答案 3 :(得分:-4)
老实说,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
此外,你应该尝试在生产中运行相同的内容,看看你得到了什么样的性能,因为很多缓存都在生产中继续进行,但不一定会在开发中发生。