Ruby on rails 如果关联具有limit子句,则AssociationRelation属性上的Rails sum不正确

Ruby on rails 如果关联具有limit子句,则AssociationRelation属性上的Rails sum不正确,ruby-on-rails,ruby-on-rails-5,arel,Ruby On Rails,Ruby On Rails 5,Arel,我有一种计算统计数据的方法,主要是对模型中的一些浮点属性求和 模型 class GroupPlayer < ActiveRecord::Base belongs_to :group has_many :scored_rounds has_many :rounds, dependent: :destroy end class Round < ActiveRecord::Base belongs_to :group_player end class ScoredRou

我有一种计算统计数据的方法,主要是对模型中的一些浮点属性求和

模型

class GroupPlayer < ActiveRecord::Base
  belongs_to :group
  has_many :scored_rounds
  has_many :rounds, dependent: :destroy
end

class Round < ActiveRecord::Base
  belongs_to :group_player
end

class ScoredRound < Round
  # STI
end
如果玩家没有赢得那场比赛,我在分数榜中总结的4个属性中的3个可能不会被设置为零,因此轮数会被过滤

一切都很顺利,直到我决定增加一个限制使用多少轮。例如,如果我只想要传递给方法_stats的查询中最后25轮的状态,我会调用:

def money_stats(grp,method,limit=100)
  sr = self.scored_rounds.where.not(method => nil).order(:date).reverse_order.limit(limit)
  method_stats(method,sr,grp)
end
同样,我只是在查询中添加了limit和order子句。所有的记录都很好

如果我在控制台中模拟该过程而不使用上述方法或使用它们!我会得到一个错误的总数

gp = GroupPlayer.find(123)
  GroupPlayer Load (2.1ms)  SELECT  "group_players".* FROM "group_players" WHERE "group_players"."id" = $1 LIMIT $2  [["id", 123], ["LIMIT", 1]]
  => valid group player

sr = gp.scored_rounds.where.not(:quality => nil)
  ScoredRound Load (1.7ms)  SELECT "rounds".* FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL)  [["group_player_id", 123]]

  => #<ActiveRecord::AssociationRelation [#<ScoredRound id: 5706, player_id: 123, group_player_id: 123, event_id: 12, type: "ScoredRound", date: "2016-11-04", team: 3, tee: "White", quota: 32, front: 15, back: 15, total: 30, created_at: "2016-11-04 14:18:27", updated_at: "2016-11-04 19:12:47", quality: 0.0, skins: nil, par3: nil, other: nil>,...]

sr.count
   (1.5ms)  SELECT COUNT(*) FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL)  [["group_player_id", 123]]
  => 44

sr.sum(:quality)
   (1.0ms)  SELECT SUM("rounds"."quality") FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL)  [["group_player_id", 123]]
  => 354.166666666667

# Now if I add the order and limit clause

sr = gp.scored_rounds.where.not(:quality => nil).order(:date).reverse_order.limit(25)
  ScoredRound Load (1.6ms)  SELECT  "rounds".* FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL) ORDER BY "rounds"."date" DESC LIMIT $2  [["group_player_id", 123], ["LIMIT", 25]]
  => => #<ActiveRecord::AssociationRelation [...]

sr.count
   (1.1ms)  SELECT COUNT(count_column) FROM (SELECT  1 AS count_column FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL) LIMIT $2) subquery_for_count  [["group_player_id", 123], ["LIMIT", 25]]
=> 25

sr.sum(:quality)
   (1.8ms)  SELECT  SUM("rounds"."quality") FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL) LIMIT $2  [["group_player_id", 123], ["LIMIT", 25]]
=> 354.166666666667

###  This is the error, it return the sum off all records, 
# not the limited???? if I use pluck and sum

sr.pluck(:quality)
=> [10.0, 11.3333333333333, 10.0, 34.0, 0.0, 7.33333333333333, 0.0, 0.0, 31.5, 0.0, 21.3333333333333, 0.0, 19.0, 0.0, 0.0, 7.5, 0.0, 20.0, 10.0, 28.0, 8.0, 9.5, 0.0, 3.0, 24.0]

sr.pluck(:quality).sum
=> 254.49999999999994
我不知道我是在阿雷尔发现了一个漏洞还是我做错了什么。我尝试了一轮,而不是STI得分,结果是一样的

有什么想法吗

# File activerecord/lib/active_record/relation/calculations.rb, line 75
def sum(column_name = nil)
  return super() if block_given?
  calculate(:sum, column_name)
end
如果调用sr.sum:quality,那么sum将quality作为列名,并计算给定列上的值之和

如果您注意到,无论有无限制,两者的总和结果都是相同的:

sr = gp.scored_rounds.where.not(:quality => nil)
sr.sum(:quality)
   (1.0ms)  SELECT SUM("rounds"."quality") FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL)  [["group_player_id", 123]]
  => 354.166666666667

sr = gp.scored_rounds.where.not(:quality => nil).order(:date).reverse_order.limit(25)
sr.sum(:quality)
   (1.8ms)  SELECT  SUM("rounds"."quality") FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL) LIMIT $2  [["group_player_id", 123], ["LIMIT", 25]]
=> 354.166666666667
这是因为LIMIT会影响查询返回的行数,SUM只返回一行,所以该函数将应用于所有44条记录,而不是指定给LIMIT的25条记录。sr.pulk:quality.sum的情况并非如此,它只适用于查询返回的25条记录

我不知道我是在阿雷尔发现了一只虫子还是我做错了什么


遗憾的是,99.9%的时间不是错误,而是我们的错误:

谢谢你的解释。事实上,我发现了一篇有一年历史的帖子,它问了同样的问题,并给出了一个解释,说明限制并不是问题的一部分。
sr = gp.scored_rounds.where.not(:quality => nil)
sr.sum(:quality)
   (1.0ms)  SELECT SUM("rounds"."quality") FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL)  [["group_player_id", 123]]
  => 354.166666666667

sr = gp.scored_rounds.where.not(:quality => nil).order(:date).reverse_order.limit(25)
sr.sum(:quality)
   (1.8ms)  SELECT  SUM("rounds"."quality") FROM "rounds" WHERE "rounds"."type" IN ('ScoredRound') AND "rounds"."group_player_id" = $1 AND ("rounds"."quality" IS NOT NULL) LIMIT $2  [["group_player_id", 123], ["LIMIT", 25]]
=> 354.166666666667