Ruby Rails Complex SQL с агрегатной функцией и DayOfWeek - PullRequest
1 голос
/ 03 февраля 2011

Рельсы 2.3.4

Я искал в Google и не нашел ответа на мою дилемму.

Для этого обсуждения у меня есть две модели. Пользователи и записи. У пользователей может быть много записей (по одной на каждый день).

Записи имеют значения и даты отправления.

Я хочу запросить и отобразить среднее значение записей для пользователя BY DAY OF WEEK. Поэтому, если пользователь ввел значения, скажем, за последние 3 недели, я хочу показать среднее значение по воскресеньям, понедельникам и т. Д. В MySQL это просто:

SELECT DAYOFWEEK(sent_at) as day, AVG(value) as average FROM entries WHERE user_id = ? GROUP BY 1

Этот запрос будет возвращать от 0 до 7 записей, в зависимости от того, сколько дней у пользователя была хотя бы одна запись.

Я посмотрел на find_by_sql, но пока я ищу Entry, я не хочу возвращать объект Entry; вместо этого мне нужен массив до 7 дней и в среднем ...

Кроме того, меня немного беспокоит производительность, так как мы хотели бы загрузить ее в пользовательскую модель, когда пользователь входит в систему, чтобы ее можно было отобразить на его приборной панели. Любые советы / указатели приветствуются. Я относительно новичок в Rails.

Ответы [ 2 ]

2 голосов
/ 03 февраля 2011

Вы можете запросить базу данных напрямую, нет необходимости использовать фактический объект ActiveRecord. Например:

ActiveRecord::Base.connection.execute "SELECT DAYOFWEEK(sent_at) as day, AVG(value) as average FROM entries WHERE user_id = #{user.id} GROUP BY DAYOFWEEK(sent_at);"

Это даст вам MySql :: Result или MySql2 :: Result, которые вы затем сможете использовать все или все в этом перечислимом для просмотра ваших результатов.

Что касается кеширования, я бы порекомендовал использовать memcached, но любая другая стратегия кеширования rails также подойдет. Хорошим преимуществом memcached является то, что срок действия кэша может истечь через определенное время. Например:

result = Rails.cache.fetch('user/#{user.id}/averages', :expires_in => 1.day) do
  # Your sql query and results go here
end

Это поместит ваши результаты в memcached на один день под ключом «user // средние». Например, если бы вы были пользователем с идентификатором 10, ваши средние значения были бы в memcached под 'user / 10 / average', и в следующий раз, когда вы пошли выполнять этот запрос (в течение того же дня), кэшированная версия была бы использована вместо фактического нажатия базы данных.

0 голосов
/ 03 февраля 2011

Не проверено, но что-то вроде этого должно работать:

@user.entries.select('DAYOFWEEK(sent_at) as day, AVG(value) as average').group('1').all

ПРИМЕЧАНИЕ : Когда вы используете select для явного указания столбцов, возвращаемые объекты только для чтения .Rails не может надежно определить, какие столбцы можно и нельзя изменять.В этом случае вы, вероятно, не пытаетесь изменить выбранные столбцы, но вы можете изменить свои столбцы sent_at или value через полученные объекты либо.

Проверьте ActiveRecordЗапрос руководства для ознакомления с тем, что здесь происходит, в довольно дружественном для новичков формате.Да, и если этот запрос не работает, пожалуйста, отправьте ответ, чтобы другие, которые могут наткнуться на это, могли это видеть (и я, возможно, смогу обновить).1018 * возвращая массив, мы можем вместо этого попробовать join:

User.where(:user_id => params[:id]).joins(:entries).select('...').group('1').all

Опять же, я не знаю, будет ли это работать.Обычно вы можете указать where после joins, но я не видел, чтобы select комбинировался там.Хитрость здесь в том, что select, вероятно, вообще исключит возвращение каких-либо данных о user.Возможно, имеет смысл просто отказаться от find_by_* методов в пользу написания метода в модели Entry, который просто вызывает ваш запрос с помощью select_all ( docs ) и пропускает сопоставление ассоциации.

...