简体   繁体   English

如何在Rails活动记录查询中执行条件计数?

[英]How to perform a conditional count in a Rails active record query?

In a Rails 3.2 app I'm trying to construct a query that will return an array with two calculated counts. 在Rails 3.2应用程序中,我正在尝试构建一个查询,该查询将返回具有两个计算计数的数组。 The app has three models: a User has many Events, and a Location has many Events. 该应用程序有三个模型:一个用户有很多事件,一个位置有很多事件。 I need to return an array for a user that contains the number of events they have at each location, as well as the number of active events. 我需要为用户返回一个数组,其中包含每个位置的事件数以及活动事件数。

eg, [#<Location id: 1, name: "Location Name", total_events_count: 4, active_event_count: 4>]> 例如, [#<Location id: 1, name: "Location Name", total_events_count: 4, active_event_count: 4>]>

I can get the total_event_count 我可以得到total_event_count

user = User.find(params[:id])
user.locations
  .select("locations.id, locations.name, count(events.id) AS total_events_count")
  .joins(:events)
  .group("locations.id")

Given that my Event model has a string status field that can take a value active , how would I also include an active_events_count in this query? 鉴于我的Event模型有一个字符串status字段可以active值,我如何在此查询中包含active_events_count?

EDIT 编辑

After some useful suggestions from xdazz and mu, I'm still struggling with this. 在得到xdazz和mu的一些有用的建议后,我仍然在努力解决这个问题。 My problem appears to be that the count is counting all events, rather than events that belong to the location AND the user. 我的问题似乎是计数正在计算所有事件,而不是属于该位置和用户的事件。

I'm going to try to rephrase my question, hopefully someone can help me understand this. 我打算试着改写我的问题,希望有人可以帮我理解这个。

Events belong to both a User and a Location (ie User has_many :locations, through: :events ) 事件属于用户和位置(即User has_many :locations, through: :events

Events have several fields, including status (a string) and participants (an integer). 事件有几个字段,包括status (字符串)和participants (整数)。

In a User show view, I'm trying to generate a list of a User's locations. 在用户节目视图中,我正在尝试生成用户位置列表。 For each location, I want to display a "success rate". 对于每个位置,我想显示“成功率”。 The success rate is the total number of a User;s events with participants, divided by the total number of that User's events. 成功率是用户的总数;与参与者的事件除以用户事件的总数。 ie, if User1 has 4 events at LocationA, but only two of those events had participants, User1's success rate at LocationA is 0.5 (or 50%). 即,如果User1在LocationA有4个事件,但这些事件中只有两个有参与者,则User1在LocationA的成功率为0.5(或50%)。

The way I though to achieve this is via a select query that also includes calculated counts for total_events_count and successful_events_count . 我实现这一点的方法是通过一个select查询,该查询还包括total_events_countsuccessful_events_count计算计数。 (there may be a better way?) (可能有更好的方法?)

So I do something like: 所以我做了类似的事情:

user = User.find(params[:id])
user.locations
  .select("locations.id, locations.name, count(events.id) AS total_events_count, sum(case events.marked when NOT 0 then 1 else 0 end) AS successful_events_count")
  .joins(:events)
  .group("locations.id")

This is returning an array with the correct keys, but the values are not correct. 这将返回具有正确键的数组,但值不正确。 I am getting the total number of all events (and all successful events) for that location, not just a count of those events that belong to the current user. 我获取该位置的所有事件(以及所有成功事件)的总数,而不仅仅是属于当前用户的那些事件的计数。

I have been looking at this problem for so long that I'm getting myself very confused. 我一直在看这个问题,以至于让自己很困惑。 Would be very grateful for a fresh perspective and ideas!! 非常感谢新的观点和想法!

EDIT2 EDIT2

After a break and fresh eyes, I have managed to get the result I need using the following code. 经过休息和新鲜的眼睛,我已经设法使用以下代码获得我需要的结果。 It seems quite convoluted. 这看起来很复杂。 If there is a better way, please let me know. 如果有更好的方法,请告诉我。 Otherwise I will tidy up this question in case anyone else runs into the same problem. 否则我会整理这个问题以防其他人遇到同样的问题。

class User
def location_ratings
  events = self.events
  successful_events = events.where('events.participants > 0')
  user_events_by_location = Event.
    select("events.location_id AS l_id, count(events.id) AS location_event_count").
    where( id: events.pluck(:id) ).
    group("l_id").
    to_sql
  user_successful_events_by_location = Event.
    select("events.location_id AS l_id, count(events.id) AS location_successful_events_count").
    where( id: successful_events.pluck(:id) ).
    group("l_id").
    to_sql
  Location.
    joins("JOIN (#{user_events_by_location}) AS user_events ON user_events.l_id = location.id").
    joins("JOIN (#{user_successful_events_by_location}) AS successful_user_events ON successful_user_events.l_id = location.id").
    select('location.id, location.name, user_events.location_events_count, successful_user_events.location_successful_events_count').
    order("user_events.location_events_count DESC")
end

You could use sum(events.marked='active') to get it: 您可以使用sum(events.marked='active')来获取它:

user.locations
  .select("locations.id, locations.name, count(events.id) AS total_events_count, sum(events.marked='active') AS marked_event_count")
  .joins(:events)
  .group("locations.id")

Update: 更新:

If you are using postgresql, then you have to case boolean to int before using SUM function. 如果您使用的是postgresql,那么在使用SUM函数之前必须将boolean设置为int。

user.locations
  .select("locations.id, locations.name, count(events.id) AS total_events_count, sum((events.marked='active')::int) AS marked_event_count")
  .joins(:events)
  .group("locations.id")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM