How to use `to_sql` in AREL when using `average()`?

后端 未结 1 1015
旧巷少年郎
旧巷少年郎 2020-12-10 12:31

I am trying to the get SQL from AREL, but it does not work in case I use average(:stars) :

This works:

Review.where(\"reviewed_user_id =         


        
相关标签:
1条回答
  • 2020-12-10 13:09

    The reason this is happening is because the average method is on ActiveRecord::Relation, not Arel, which forces the computation.

    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]
    

    By checking out the internals of ActiveRecord::Calculations, you can derive how to get at the SQL that it uses.

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

    Careful if you're working at the console. ActiveRecord::Relation caches things so if you type the above into the console line by line, it will actually not work, because pretty-printing forces the relation. Separating the above by semicolons and no new lines, however, will work.

    Alternatively, you can use Arel directly, like so:

    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 讨论(0)
提交回复
热议问题