【问题标题】:Method cannot return Average with Rails ActiveRecord + Raw SQL方法无法使用 Rails ActiveRecord + Raw SQL 返回平均值
【发布时间】:2017-06-22 07:13:39
【问题描述】:

我正在通过构建 Rails 应用程序来学习如何编码(在完成 railstutorial.org 之后),但我遇到了一个奇怪的问题。

该应用程序旨在列出午餐(员工可以在其中投票决定他们是否喜欢午餐。)午餐属于供应商,供应商属于美食。

该应用程序也(尝试)成为多租户,因此有一个与公司相关联的 Member 模型。

午餐中所有“正面赞”的总和除以午餐票总数得出的午餐就是午餐分数。

所有的午餐记录,因此有一个provider_id、member_id 和lunchscore 属性。

我的问题: 我正在尝试获取与特定供应商相关的许多午餐的平均午餐分数。这就是我的 Provider.rb 模型中的内容:

def average_member_lunch_score(member_id)
    result = self.lunches.select("AVG(lunchscore) as average_lunchscore").where("date <= ? AND member_id = ? AND lunchscore > 0", Time.zone.now.beginning_of_day, member_id)
    return result.average_lunchscore
end

当我对提供者运行它时

arbys = Provider.find(id:2)
arbys.average_member_lunch_score(2)

我得到以下结果:

NoMethodError:   Lunch Load (0.1ms)  SELECT AVG(lunchscore) as average_lunchscore FROM "lunches" WHERE "lunches"."provider_id" = ? AND (date <= '2017-02-05 05:00:00.000000' AND member_id = 2 AND lunchscore > 0)  ORDER BY date ASC  [["provider_id", 1]]
undefined method `average_lunchscore' for #<ActiveRecord::AssociationRelation [#<Lunch id: nil>]>

但是,如果我尝试手动梳理 average_member_lunch_score 方法,并编写 SQL 查询本身,我似乎已经了解了大部分情况:

arbys.lunches.select("AVG(lunchscore) as average_lunchscore").where("date <= ? AND member_id = ? AND lunchscore > 0", Time.zone.now.beginning_of_day, 2)
  Lunch Load (0.1ms)  SELECT AVG(lunchscore) as average_lunchscore FROM "lunches" WHERE "lunches"."provider_id" = ? AND (date <= '2017-02-05 05:00:00.000000' AND member_id = 2 AND lunchscore > 0)  ORDER BY date ASC  [["provider_id", 1]]
+----+--------------------+
| id | average_lunchscore |
+----+--------------------+
|    | 0.8136429227338321 |
+----+--------------------+
1 row in set

在我看来,这可能与返回方法有关,或者它正在为“id”返回一个空白值,导致该方法失败,而查询本身似乎是可靠的。

我的最终目标是能够在视图中使用这个平均值,所以我可以输入如下内容:

<%= @provider.average_member_lunch_score(current_user.member_id) %> 

谁能帮助解释为什么查询有效,但方法失败?另外,为什么查询返回一个“id”列,即使它不在我的选择语句中?

【问题讨论】:

    标签: mysql ruby-on-rails ruby sqlite activerecord


    【解决方案1】:

    在其他人的帮助下,我最终回答了这个问题。将其分解为原始 SQL 似乎存在问题,因此使用内置的活动记录平均方法使这变得更容易(并且工作!)

    def average_member_lunch_score(member_id)
        return self.lunches.where("date <= ? AND member_id = ? AND lunchscore > 0", Time.zone.now.beginning_of_day, member_id).average("lunchscore")
    end
    

    【讨论】:

      【解决方案2】:

      在 SQL 中,聚合是在 WHERE 之后完成的;要过滤聚合值,您必须改用HAVING

      lunches.select("AVG(lunchscore) as average_lunchscore").
          where("date <= ? AND member_id = ?", ...).
          having("AVG(lunchscore) > 0")
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-22
        • 1970-01-01
        • 1970-01-01
        • 2014-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-01
        相关资源
        最近更新 更多