2011-03-02 78 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

相关问题