Как использовать `to_sql` в AREL при использовании` average () `? - PullRequest
14 голосов
/ 02 марта 2011

Я пытаюсь получить SQL от AREL, но он не работает, если я использую average(:stars):

Это работает:

Review.where("reviewed_user_id = ?", self.reviewed_user_id).to_sql
#=> "SELECT `reviews`.* FROM `reviews` WHERE (reviewed_user_id = 3)"

Это вызывает NoMethodError:

Review.where("reviewed_user_id = ?", self.reviewed_user_id).average(:stars).to_sql
#=> undefined method `to_sql' for 3:Fixnum

Так что это означает, что to_sql вызывается по результату AREL, а не по объекту AREL - но почему?

Как получить сгенерированный SQL?

1 Ответ

24 голосов
/ 03 марта 2011

Причина, по которой это происходит, заключается в том, что усредненный метод находится на ActiveRecord::Relation, а не на Arel, что вызывает вычисления.

m = Review.where('id = ?', 42).method(:average)
#=> #<Method: ActiveRecord::Relation(ActiveRecord::Calculations)#average>
m.source_location  # or m.__file__ if you're on a different version of Ruby
#=> ["/Users/jtran/.rvm/gems/ruby-1.9.2-p0/gems/activerecord-3.0.4/lib/active_record/relation/calculations.rb", 65]

Проверяя внутренние компоненты ActiveRecord::Calculations, вы можете получить, какчтобы получить SQL, который он использует.

my_reviewed_user_id = 42
relation = Review.where('reviewed_user_id = ?', my_reviewed_user_id)
column = Arel::Attribute.new(Review.unscoped.table, :stars)
relation.select_values = [column.average]
relation.to_sql
#=> "SELECT AVG(\"reviews\".\"stars\") AS avg_id FROM \"reviews\" WHERE (reviewed_user_id = 42)"

Осторожно, если вы работаете за консолью.ActiveRecord::Relation кэширует вещи, так что если вы напечатаете вышеприведенное в консоли построчно, это на самом деле не будет работать, потому что симпатичная печать заставляет отношения.Однако разделение вышеприведенных точек с запятой и отсутствие новых строк сработает.

Кроме того, вы можете использовать Arel напрямую, например:

my_reviewed_user_id = 42
reviews = Arel::Table.new(:reviews)
reviews.where(reviews[:reviewed_user_id].eq(my_reviewed_user_id)).project(reviews[:stars].average).to_sql
#=> "SELECT AVG(\"reviews\".\"stars\") AS avg_id FROM \"reviews\" WHERE \"users\".\"reviewed_user_id\" = 42"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...