Ruby on Rails Model.find():两个查询而不是子查询

时间:2012-05-21 16:26:35

标签: ruby-on-rails sqlite

我从我的SQLite数据库中使用Ruby on Rails 2.3.5接收了一组模型。目前我正在使用子查询,以便只选择我真正想要的那些:

find(:all, 
  :conditions => "foo.id in (
                    SELECT bar.foo_id FROM bar
                    JOIN baz
                      ON baz.bar_id = bar.id
                    WHERE baz.bla in (1, 2, 3)
                  )"
)

我知道这是愚蠢的,因为子查询为每一行评估相同的结果,我猜SQLite不够聪明,不能注意到它可以被优化。所以我想把它从查询中删除。我知道我可以使用第二个find()简单地从Rails调用它,但是鉴于子查询的返回值可能变得相对较大,我想在数据库中做所有事情,所以我不需要在数据库和Rails之间处理大量数据。

那么,我怎么能做模型的find()并告诉它,它应该查找我的实际子查询的返回值,然后使用它的结果来比较我的其他行?我可以通过find()的一次调用将其传递给SQLite吗?

3 个答案:

答案 0 :(得分:0)

我想知道你是否可以做以下事情:

    subs = connection.select_rows("SELECT bar.foo_id FROM bar
                                   JOIN baz
                                   ON baz.bar_id = bar.id
                                   WHERE baz.bla in (1, 2, 3)")
    find(:all, :conditions => "foo.id in (#{subs.join(',')})")

我试图在Rails 3中重写它。

答案 1 :(得分:0)

通常,您应该尝试在模型中定义关系。这样您就可以轻松使用ActiveRecord的助手

class Foo < ActiveRecord::Base
  self.table_name = "foo"

  has_many :bars
end

class Bar < ActiveRecord::Base
  self.table_name = "bar"

  belongs_to :foo
  has_many :bazs
end

class Baz < ActiveRecord::Base
  self.table_name = "baz"

  belongs_to :bar
end

使用这些模型,您可以编写与此类似的查询:

Foo.find(:all, :include => {:bar => :baz}, :conditions => ["#{Baz.table_name}.id IN (?)", [1,2,3]])

答案 2 :(得分:0)

鉴于以下模型:

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

  attr_accessible :title
end


class Bar < ActiveRecord::Base
  belongs_to :foo
  has_many :bazs

  attr_accessible :title
end


class Baz < ActiveRecord::Base
  belongs_to :bar

  attr_accessible :title
end

你可以:

Foo.all(
  :joins => { :bars => :bazs },
  :conditions => { :bars => { :bazs => { :id => [1000] } } }
)

1000是您感兴趣的Baz的ID。

这将生成以下SQL:

SELECT "foos".* FROM "foos" INNER JOIN "bars" ON "bars"."foo_id" = "foos"."id" INNER JOIN "bazs" ON "bazs"."bar_id" = "bars"."id" WHERE "bazs"."id" IN (1000)

如果你这样做,那是相同的:

Foo.all(
  :joins => { :bars => :bazs },
  :conditions => { :bazs => { :id => [1000] } }
)

甚至更好,使用through关联:

Foo.all(
  :joins => [:bazs],
  :conditions => { :bazs => { :id => [1000] } }
)

结果将是符合该条件的所有Foo记录,并且SQL将是相同的。

希望能回答你的问题。