#ruby-on-rails
#ruby-on-rails
Вопрос:
У меня есть канал модели. Соответствующая таблица содержит несколько столбцов, например, клики.
Так Channel.all.sum(:clicks)
что дает мне сумму кликов по всем каналам.
В моей модели я добавил новый метод
def test
123 #this is just an example
end
Итак, теперь Channel.first.test
возвращает 123
То, что я хочу сделать, это что-то вроде Channel.all.sum(:test)
, которое суммирует тестовое значение всех каналов.
Ошибка, которую я получаю, заключается в том, что test не является столбцом, что, конечно, не так, но я надеялся, что смогу собрать эту сумму.
Как я мог этого добиться?
Комментарии:
1. можем ли мы увидеть реализацию метода тестирования? Возможно, это можно было бы сделать на уровне БД и сэкономить много производительности
Ответ №1:
Вы могли бы попробовать:
Channel.all.map(amp;:test).sum
Где clicks — это столбец таблицы модели, используйте:
Channel.sum(:clicks)
Комментарии:
1. Потрясающе, что это уже сработало. Я отредактировал свой вопрос, потому что на самом деле он даже немного сложнее.
Ответ №2:
Чтобы решить вашу проблему, вы можете сделать
Channel.all.sum(amp;:test)
Но было бы лучше попытаться достичь этого на уровне базы данных, потому что обработка с помощью Ruby может быть тяжелой для памяти и эффективности.
Редактировать
Если вы хотите суммировать с помощью метода, который принимает аргументы:
Channel.all.sum { |channel| channel.test(start_date, end_date) }
Комментарии:
1. Я хотел бы, но значение, которое возвращает test, является вычислением с некоторыми другими факторами и более простым значением.
Ответ №3:
То, о чем вы здесь говорите, — это две очень разные вещи:
ActiveRecord::Calculations.sum
суммирует значения столбца в базе данных:
SELECT SUM("table_name"."column_name") FROM "column_name"
Это то, что происходит, если вы вызываете Channel.sum(:column_name)
.
ActiveSupport также расширяет перечислимый модуль с .sum
помощью метода:
module Enumerable
def sum(identity = nil, amp;block)
if block_given?
map(amp;block).sum(identity)
else
sum = identity ? inject(identity, : ) : inject(: )
sum || identity || 0
end
end
end
Это перебирает все значения в памяти и складывает их вместе.
Channel.all.sum(amp;:test)
Эквивалентно:
Channel.all.inject(0) { |sum, c| sum c.test }
Использование более позднего может привести к серьезным проблемам с производительностью, поскольку оно извлекает все данные из базы данных.
Ответ №4:
В качестве альтернативы вы делаете это.
Channel.all.inject(0) {|sum,x| sum x.test }
Вы можете изменить 0 на любое значение, с которого вы хотите, чтобы сумма начиналась.