还是这段代码

@stats = ParticipantStat.where(round: @rounds, round_stat: %w(win draw loss)).group('participant_id')
  .select(
    <<-EOT
      participant_id,
      COUNT(round_id) as round_count,
      SUM(CASE WHEN round_stat IN (1) THEN 1 ELSE 0 END) as winning_round_count,
      SUM(CASE WHEN mvp THEN 1 ELSE 0 END) as total_mvp,
      SUM(point) AS total_point,
      SUM(rebound) AS total_rebound,
      SUM(assist) AS total_assist,
      SUM(score) AS total_score
    EOT
      ).order('total_score desc, total_point desc, total_rebound desc, total_assist desc, total_mvp desc').includes(participant: :profile)

这里的用了select 方法。按照 API 的介绍,这个 select 方法后面可以有两种用法,一种是带一个匿名函数,例如

Model.all.select { |m| m.field == value }

这样的用法,得到是一个数组,而不是一个 relation object,不可以再带方法链。

另一种方法就是当做 SQL 的 SELECT 来用,这样得到的是一个 relation object,后面可以继续带方法链。比如

Model.select(:field, :other_field, :and_one_more)

这样得到的 Model 的对象会只带有三个属性,其他没有被选出来的属性无法调用,比如

Model.select(:field).first.other_field
# => ActiveModel::MissingAttributeError: missing attribute: other_field

如果不用 symbol,而是用字符串,这些字符串就会原封不动地转换成 SELECT 语句进行 SQL 查询

Model.select('field AS field_one', 'other_field AS field_two')

也就是说,字符串需要符合 SQL 语法。

读到这里,前面的代码就能看懂了,里面用到 SQL 语句有

COUNT(...) AS XXX
SUM(...) AS XXX
SUM(CASE WHEN XXX THEN XXX ELSE XXX END) AS XXX

AS 后面的可以看做是一个虚拟属性,这样就可以根据需要定制出不用条件下的属性。