使用Activerecord对多列进行求和

时间:2014-02-20 18:06:42

标签: ruby-on-rails activerecord

我是Activerecord的新手。我想对模型学生的多个列进行求和。我的模范学生就像是:

 class Student < ActiveRecord::Base
   attr_accessible :class, :roll_num, :total_mark, :marks_obtained, :section
 end

我想要这样的东西:

 total_marks, total_marks_obtained = Student.where(:id=>student_id).sum(:total_mark, :marks_obtained)

但它给出了以下错误。

NoMethodError: undefined method `except' for :marks_obtained:Symbol

所以我问我是否必须对上面的模型进行两次查询,即一次查找总标记,另一次查找获得的标记。

5 个答案:

答案 0 :(得分:14)

如果需要,您可以使用原始SQL。像这样返回一个你必须提取值的对象...我知道你指定了活动记录!

Student.select("SUM(students.total_mark) AS total_mark, SUM(students.marks_obtained) AS marks obtained").where(:id=>student_id)

对于rails 4.2(之前未选中)

Student.select("SUM(students.total_mark) AS total_mark, SUM(students.marks_obtained) AS marks obtained").where(:id=>student_id)[0]

注意声明后面的括号。没有它,语句将返回Class :: ActiveRecord_Relation,而不是AR实例。有意义的是,你不能在关系上使用first

....where(:id=>student_id).first #=> PG::GroupingError: ERROR:  column "students.id" must appear in the GROUP BY clause or be used in an aggregate function

答案 1 :(得分:11)

您可以使用pluck直接获取总和:

Student.where(id: student_id).pluck('SUM(total_mark)', 'SUM(marks_obtained)')
# SELECT SUM(total_mark), SUM(marks_obtained) FROM students WHERE id = ?

您可以将所需的列或计算字段添加到pluck方法,它将返回一个包含值的数组。

答案 2 :(得分:8)

如果你只想要总和列total_marks和marks_obtained,请试试这个

Student.where(:id=>student_id).sum('total_mark + marks_obtained')

答案 3 :(得分:3)

另一种方法是在外部数组上再ActiveRecord::Calculations.pluck然后Enumerable#sum,在内部数组对上再次使用regex match

Student
  .where(id: student_id)
  .pluck(:total_mark, :marks_obtained)
  .map(&:sum)
  .sum

生成的SQL查询很简单:

SELECT "students"."total_mark",
       "students"."marks_obtained"
FROM "students"
WHERE "students"."id" = $1

pluck的初始结果将是一个数组对的数组,例如:

[[10, 5], [9, 2]]

.map(&:sum)会在每对上运行sum,总计对并展平数组:

[15, 11]

最后在展平数组上的.sum将产生一个值。

修改

请注意,虽然只有一个查询,但您的数据库将为where中匹配的每条记录返回结果行。这个方法使用ruby来进行总计,所以如果有很多记录(即数千个),这个可能比让SQL进行计算本身要慢,如接受的答案中所述。

答案 4 :(得分:1)

但是,与接受的答案类似,我建议如下使用arel以避免字符串文字(除了重命名列,如果需要的话)。

Student
  .where(id: student_id).
  .where(Student.arel_table[:total_mark].sum, Student.arel_table[:marks_obtained].sum)

这将为您提供ActiveRecord::Relation的结果,您可以在该结果上进行迭代,或者,由于只有一行,您可以使用.first(至少对于 mysql )。