ассоциация, сопровождаемая именованной областью, приводит к дублированию запроса - PullRequest
0 голосов
/ 10 февраля 2011

Я столкнулся с этой странной проблемой на работе, поэтому я создал минимальное приложение, чтобы выделить проблему. У меня есть две модели с простой ассоциацией:

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()

Я ожидаю, что рельсы сгенерируют один 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)) 

Первый запрос в основном бесполезен и может занимать очень много времени в случае больших коллекций.

У кого-нибудь есть идея, почему рельсы так себя ведут?

Обратите внимание, что вместо того, чтобы делать

p.children.only_adults.all()

Я делаю

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

где by_parent - именованная область, тогда я получаю только один запрос.

Также обратите внимание на дублирование условия parent_id. Это не имеет большого значения.

Спасибо за ваш отзыв.

1 Ответ

3 голосов
/ 10 февраля 2011

Причина в способе выполнения запросов в Rails 2.3.x, в отличие от Rails 3.0.x.В Rails 2 вызов p.children автоматически выполнит запрос, независимо от именованных областей, которые вы прикрепили к остальной части запроса.Единственный способ обойти это - использовать named_scopes в комбинации, как вы делаете это в «Child.by_parent (p.id) .only_adults.all ()», поскольку именованные области сохраняют такое поведение, откладывая запрос.В Rails 3 запрос создается до тех пор, пока не будет найдено ключевое слово execute (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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...