在Rails中创建质量HABTM关联的最快方法是什么?

问题描述:

我有两个表,在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.bars = @bars 
@foo.bars << @bars 

而且两者运行速度很慢,与像每个bar以下的条目:

bars_foos列(1.1ms)显示 FIELDS FROM bars_foos SQL(0.6ms) INSERT INTO bars_foosbar_idfoo_id)VALUES(100,117200)

我看着ar-extensions,但import函数在没有模型(Model.import)的情况下似乎无法使用,该模型不适用于连接表。

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

+0

真的吗?没有人?你们跳过所有的上篮和“最佳练习”问题:) – klochner 2010-02-04 22:32:30

我认为你最好的性能表现就是使用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”栏,请确保您有场在你的数据库索引。

+0

我的mass_habtm只是将查询合并到一个搜索/插入查询中,这可能不会获得*,远远超过你在这里的内容。我讨厌选择我自己的,所以感谢至少给我一个可行的选择。 – klochner 2010-02-08 17:28:01

这比等效本机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 

此外,你应该尝试在生产环境中运行相同的,看看你会得到什么样的表现,因为很多缓存在生产中并不一定发生在开发流程。

+1

不是一个混蛋,但你绝不会解决主要问题 - 创建关系的速度,除了猜测生产可能会更好。虽然缓存可能有所帮助,但它几乎肯定不会改变SQL的制定方式。我也认为habtm是优化这个东西的更好的选择,因为has_many-> through需要一个模型类,这意味着在连接模型中可能会有回调。 – klochner 2010-02-06 21:02:41

+1

我相当肯定你的执行速度*比我提出的速度慢*速度不够快*。 – klochner 2010-02-06 21:10:48

+0

是的,但是在连接上有一个模型可以让您使用您可能无法访问的查找器和其他选项来执行其他操作。至少在这种情况下,如果你想来回穿梭,你可以使用另一种模式来放置代码,而不是在两种模型中重复使用。 – 2010-02-06 21:44:30

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