Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use several aggregate functions at once in Rails/ActiveRecord?

I want to do several aggregate functions at once, eg to get the max and min id's grouped by status:

Model.maximum(:id).minimum(:id).group(:status)

This doesnt work (at least with Rails 3.1.1) - you get an error on the minimum call, saying its not defined on Fixnum.

NoMethodError: undefined method `minimum' for 22377:Fixnum

I could do raw sql for it - but just wondering if there is a higher level/Rails option...

Thanks, Chris

like image 603
Chris Kimpton Avatar asked Nov 11 '11 21:11

Chris Kimpton


People also ask

Can we use aggregate function in another aggregate function?

SQL does not permit the application of an aggregate function to another aggregate function or a subquery, so the only ways I can think to do this are by using a temporary table or by iterating through the result set and manually calculating the values.

Do aggregate functions require group by?

The only column names that can be displayed, along with aggregate functions, must be listed in the GROUP BY clause.

What is N 1 query problem rails?

The n+1 query problem is one of the most common scalability bottlenecks. It involves fetching a list of resources from a database that includes other associated resources within them. This means that we might have to query for the associated resources separately.

What is ActiveRecord naming convention?

Active Record uses naming conventions for the columns in database tables, depending on the purpose of these columns. Foreign keys - These fields should be named following the pattern singularized_table_name_id (e.g., item_id , order_id ).


2 Answers

I've had a similar issue which I solved using group with pluck in Rails 4. E.g.

Model.group(:status).pluck('min(id)','max(id)')

Also works with having, order etc and with computed columns.

Model.group('CAST(created_at AS Date), EXTRACT(HOUR FROM created_at), floor(EXTRACT(MINUTE FROM created_at)/15) * 15')
     .having('count(id) > 10')
     .order('min(created_at)')
     .pluck('count(id)')

Cheers

like image 95
Andrew Pietsch Avatar answered Oct 04 '22 05:10

Andrew Pietsch


I believe @topek is right that you can't chain the calculation functions like this. I think you'll have to use SQL in a select predicate, e.g.:

Model.select('MAX(id) AS `maximum`, MIN(id) AS `minimum`').group(:status)

I just tested this in a project of my own and it appears to work as expected.

like image 29
Jordan Running Avatar answered Oct 04 '22 03:10

Jordan Running