2011-03-02 82 views
13

我試圖從AREL的get SQL,但它不能在情況下工作,我用average(:stars)如何在使用`average()`時在AREL中使用`to_sql`?

這工作:

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?

回答

24

發生這種情況的原因是因爲平均方法是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緩存的東西,所以如果你在控制檯中逐行輸入上述內容,它實際上將不起作用,因爲漂亮打印會強制關係。然而,用分號分隔上面的內容並不需要換行,這將起作用。

或者,你可以直接使用阿雷爾,就像這樣:

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" 
+0

超!感謝您分享您用來解決這個問題的方法..將在未來派上用場! – Zabba 2011-03-03 17:09:59

+0

這就是我喜歡Ruby的原因。您可以隨時詢問REPL和來源。這是源於不止一種意義的詞語! – 2011-03-03 18:02:57

+0

這在許多不同的邏輯中幫助我很多。偉大的工作人。 – 2012-11-09 05:11:57

相關問題