关联后跟命名范围会产生重复的 SQL 查询

发布于 2024-10-17 00:16:17 字数 1021 浏览 6 评论 0原文

我在工作中遇到了这个奇怪的问题,因此我创建了一个最小的应用程序来突出显示该问题。我有两个具有简单关联的模型:

class Parent < ActiveRecord::Base
  has_many :children
end

class Child < ActiveRecord::Base
  belongs_to :parent
  named_scope :only_adults, :conditions => "adult is true"
end

现在,当我这样做时,

 p = Parent.first
 p.children.only_adults.all()

我希望 Rails 生成包含所有条件的单个 SQL 查询。但是,这是我在日志中看到的内容:

Child Load (0.5ms)   SELECT * FROM "children" WHERE ("children".parent_id = 1) 
Child Load (0.3ms)   SELECT * FROM "children" WHERE ("children".parent_id = 1) AND ((adult is true) AND ("children".parent_id = 1)) 

第一个查询基本上是无用的,并且在大型集合的情况下可能非常耗时。

有谁知道为什么 Rails 会这样?

没有执行

p.children.only_adults.all()

请注意,我

Child.by_parent(p.id).only_adults.all()

where by_parent 是命名范围的操作,而是只得到一个查询。

另请注意 Parent_id 条件的重复。这没什么大不了的。

感谢您的反馈。

I came across this strange issue at work so I created a minimal app to highlight the problem. I have two models with a simple association:

class Parent < ActiveRecord::Base
  has_many :children
end

and

class Child < ActiveRecord::Base
  belongs_to :parent
  named_scope :only_adults, :conditions => "adult is true"
end

Now when I do

 p = Parent.first
 p.children.only_adults.all()

I would expect rails to generate a single SQL query containing all the conditions. However, here's what I see in the log:

Child Load (0.5ms)   SELECT * FROM "children" WHERE ("children".parent_id = 1) 
Child Load (0.3ms)   SELECT * FROM "children" WHERE ("children".parent_id = 1) AND ((adult is true) AND ("children".parent_id = 1)) 

The first query is basically useless and can be very time-comsuming in the case of large collections.

Does anyone have an idea why rails is behaving as such?

Note that instead of doing

p.children.only_adults.all()

I do

Child.by_parent(p.id).only_adults.all()

where by_parent is a named scope, then I get only one query.

Also note the duplication of the parent_id condition. This is not a big deal.

Thank you for your feedback.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

尤怨 2024-10-24 00:16:17

原因与 Rails 2.3.x(而不是 Rails 3.0.x)中执行查询的方式有关。在 Rails 2 中,调用 p.children 将自动执行查询,无论您附加到查询其余部分的命名范围如何。解决这个问题的唯一方法是组合使用named_scopes,就像您在“Child.by_parent(p.id).only_adults.all()”中所做的那样,因为命名范围保留了推迟查询的行为。在 Rails 3 中,查询会一直构建,直到找到执行关键字(count、all、first、last),因此可以在单个查询中执行以下操作:

class Child < ActiveRecord::Base
  belongs_to :parent
  scope :only_adults, where(adult: true)
end

Parent.first.children.only_adults.all()

# SELECT * FROM "children" WHERE ("children".parent_id = 1) AND (adult is true)

The reason has to do with the way queries are executed in Rails 2.3.x, as opposed to Rails 3.0.x. In Rails 2, calling p.children will automatically execute the query, regardless of the named scopes you have attached to the rest of the query. The only way to get around this is to use named_scopes in combination, like you do in "Child.by_parent(p.id).only_adults.all()" since the named scopes keep this behavior of postponing the query. In Rails 3 the query is built up until an execute keyword is found (count, all, first, last), so it's possible to do the following in a single query:

class Child < ActiveRecord::Base
  belongs_to :parent
  scope :only_adults, where(adult: true)
end

Parent.first.children.only_adults.all()

# SELECT * FROM "children" WHERE ("children".parent_id = 1) AND (adult is true)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文