代码之家  ›  专栏  ›  技术社区  ›  klochner

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

  •  13
  • klochner  · 技术社区  · 14 年前

    我有两个表,在Rails中有一个HABTM关系。如下所示:

    class Foo &lt ActiveRecord::Base
      has_and_belongs_to_many :bars
    end
    
    class Bar &lt 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 &lt&lt @bars
    

    两个都运行得非常慢,每个都有如下条目 bar

    条形图和柱形图(1.1ms)显示 bars_foos SQL(0.6ms) 酒吧 ( bar_id foo_id )数值(100117200)

    我查看了ar扩展,但是 import

    我是否需要编写SQL,或者Rails是否有更好的方式?

    4 回复  |  直到 14 年前
        1
  •  9
  •   Peter Brown    14 年前

    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_属性”搜索工具栏,请确保在数据库中为该字段编制了索引。

        2
  •  7
  •   Pete    8 年前

    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
    
        3
  •  1
  •   klochner    13 年前

    这比等效的本机rails代码快7倍:

    class &lt&lt 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中得到有效的支持,我很想听听是否有人有更干净的方法。

    也许它在3.x中的实现效率更高?

        4
  •  -4
  •   Josh Delsman    14 年前

    真的 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
    

    此外,您应该尝试在生产环境中运行相同的缓存,看看您获得了什么样的性能,因为在生产环境中进行的大量缓存在开发过程中并不一定会发生。