Rails基于列的值按计数排序

时间:2017-07-26 05:32:57

标签: ruby-on-rails count querying

Rails 5.1.2

我这样有两个模型StudentAward

class Student < ApplicationRecord
  has_many :awards
end

class Award < Application Record
  belongs_to :students

  # Categories
  scope :attendance, -> { where(category: 0) }
  #...(other award categories)
  scope :talent,     -> { where(category: 8) }
  # Ranks
  # Gold
  scope :rank_1, -> { where(rank: 1) }
  # Silver
  scope :rank_2, -> { where(rank: 2) }
  # Bronze
  scope :rank_3, -> { where(rank: 3) }
end

Award包含以下列:rankcategory

现在,我希望获得特定类别的顶尖学生。对此的标准是,按“金”奖励(等级1)的顺序排序,然后按“银”(等级2)奖励的数量排序,然后按“铜”的数量排序(排名3)奖励。

因此,如果我要获得符合类别Student的最高标准的0(由上面模型中描述的attendance范围处理),这是我认为查询应该是这样的:

Student.joins(:awards).where(awards: { category: 0 }).group('students.id').order('COUNT(awards.rank == 1) DESC', 'COUNT(awards.rank == 2) DESC', 'COUNT(awards.rank == 3) DESC').take

但是,无论排名如何,这都会使奖励计数最高的学生返回。例如,如果我删除take,则顺序如下:

# |St.ID | Gold  | Slvr. | Brnz. |
----------------------------------
1 |  12  |   4   |   12  |   8   |
----------------------------------
2 |   1  |   9   |   0   |   4   |
----------------------------------
3 |   6  |   9   |   1   |   0   |
----------------------------------
4 |  18  |   5   |   2   |   2   |
----------------------------------
 ...

因此,我收到的订单是ID 12, 1, 6, 18, ...,当它应为ID 6, 1, 18, 12, ...时。

我意识到order('COUNT(awards.rank == 1) DESC', 'COUNT(awards.rank == 2) DESC', 'COUNT(awards.rank == 3) DESC')部分只是按奖励总数排序(而不是列rank列中具有特定值的奖励的数量。)

我可以通过为每个类别的奖项添加一个计数器缓存来轻松解决这个问题,但这不是一个优雅而灵活的解决方案。

作为奖励,在此查询返回成功结果后,我将再次搜索数据库以查找具有相同分数的所有学生(可能存在关系)。我不知道在一个查询中完成所有这些操作的方法(可能在获取每个级别的值之后通过子查询)。

1 个答案:

答案 0 :(得分:0)

我认为你的问题可能是双重平等?

编辑:这是一种更合适的方式(假设MySQL):

 Student.joins(:awards).where(awards: { category: 0 }).group('students.id').order('COUNT(if(awards.rank = 1)) DESC', 'COUNT(if(awards.rank = 2)) DESC', 'COUNT(if(awards.rank = 3)) DESC').take

首先尝试没有它的代码,如果它不起作用,你可能想尝试一下:

SELECT students.id, gold, silver, bronze) FROM students 
JOIN ON 
  (SELECT students.id as id COUNT(awards) as bronze
  FROM students JOIN awards ON students.id = awards.student_id
  WHERE awards.rank = 1
  GROUP BY students.id) q1 q1.id = students.id
JOIN ON 
  (SELECT students.id as id COUNT(awards) as silver
  FROM students JOIN awards ON students.id = awards.student_id
  WHERE awards.rank = 2
  GROUP BY students.id) q2 q2.id = students.id
JOIN ON 
  (SELECT students.id as id COUNT(awards) as gold
  FROM students JOIN awards ON students.id = awards.student_id
  WHERE awards.rank = 3
  GROUP BY students.id) q3 q3.id = students.id
ORDER BY gold, silver, bronze