简体   繁体   中英

Rails, sum the aggregate of an attribute of all instances of a model

I have a model Channel. The relating table has several column, for example clicks.

So Channel.all.sum(:clicks) gives me the sum of clicks of all channels.

In my model I have added a new method

def test
 123 #this is just an example
end

So now, Channel.first.test returns 123

What I want to do is something like Channel.all.sum(:test) which sums the test value of all channels.

The error I get is that test is not a column, which of course it is not, but I hoped to till be able to build this sum.

How could I achieve this?

You could try:

Channel.all.map(&:test).sum

Where clicks is a column of the model's table, use:

Channel.sum(:clicks)

To solve your issue, you can do

Channel.all.sum(&:test)

But it would be better to try achieving it on the database layer, because processing with Ruby might be heavy for memory and efficiency.

EDIT

If you want to sum by a method which takes arguments:

Channel.all.sum { |channel| channel.test(start_date, end_date) }

What you are talking about here is two very different things:

ActiveRecord::Calculations.sum sums the values of a column in the database:

SELECT SUM("table_name"."column_name") FROM "column_name"

This is what happens if you call Channel.sum(:column_name) .

ActiveSupport also extends the Enumerable module with a .sum method:

module Enumerable
  def sum(identity = nil, &block)
    if block_given?
      map(&block).sum(identity)
    else
      sum = identity ? inject(identity, :+) : inject(:+)
      sum || identity || 0
    end
  end 
end

This loops though all the values in memory and adds them together.

Channel.all.sum(&:test)

Is equivalent to:

Channel.all.inject(0) { |sum, c| sum + c.test }

Using the later can lead to serious performance issues as it pulls all the data out of the database.

Alternatively you do this.

Channel.all.inject(0) {|sum,x| sum + x.test }

You can changed the 0 to whatever value you want the sum to start off at.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM