我是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
所以我问我是否必须对上面的模型进行两次查询,即一次查找总标记,另一次查找获得的标记。
答案 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 )。