Ruby Rails具有聚合函数和DayOfWeek的复杂SQL

Rails 2.3.4

我搜索谷歌,并没有找到我的困境的答案。

对于这个讨论,我有两个模型。 用户和条目。 用户可以拥有多个条目(每天一个)。

条目具有值和sent_at日期。

我想查询并显示用户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比较陌生。

您可以直接查询数据库,无需使用实际的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
   #你的sql查询和结果就在这里
结束

这会在“用户//平均值”键下将结果存入memcached一天。 例如,如果您是id为10的用户,则平均值将在“user / 10 / average”下的memcached中,并且下次您执行此查询(在同一天内)时,将使用缓存版本而不是实际访问数据库。

未经测试,但这样的事情应该有效:

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

注意 :使用select显式指定列时,返回的对象是只读的 。 Rails无法可靠地确定哪些列可以修改,也不能修改。 在这种情况下,您可能不会尝试修改所选列,但您可以通过生成的对象修改sent_atvalue列。

查看ActiveRecord查询指南 ,了解这里发生了什么以相当新的友好格式进行细分。 哦,如果那个查询不起作用,请回复,以便可能偶然发现的其他人可以看到(并且我可以更新)。


由于返回数组的entries不起作用,我们可以尝试使用join

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

同样,我不知道这是否有效。 通常你可以指定joins后的位置,但我还没有看到select在那里组合。 这里的一个棘手的问题是, select可能会消除返回有关user任何数据。 避免find_by_*方法更有意义,有利于在Entry模型中编写一个只用select_all ( docs )调用查询并跳过关联映射的方法。

Interesting Posts