2017-05-13 42 views
1

我有一個方法計算模型中的多個浮點屬性的統計數據(主要是總和)。關聯關係屬性的Rails求和是不正確的,如果關聯有限制條款

該機型

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浮動屬性是從其他的方法調用,這取決於如果我得到的統計一個球員或一組球員統計方法。在ScoredRound最初的過濾器傳遞給方法(SR)

def method_stats(method,sr,grp) 
    rounds = sr.where.not(method => nil) 
    number_rounds = rounds.count 
    won = rounds.sum(method).round(2) 
    if method == :quality 
    dues = grp.options[:dues] * number_rounds 
    else 
    dues = grp.options["#{method.to_s}_dues"] * number_rounds 
    end 
    balance = (won - dues).round(2) 
    perc = dues > 0 ? (won/dues).round(3) : 0.0 
    [self.full_name,number_rounds,won,dues,balance,perc] 
end 

的4個屬性,我在ScoredRounds總結的3不能設置(無),如果玩家沒有贏得那場比賽,因此輪被過濾。

一切正常,直到我決定添加一個限制多少回合使用。舉例來說,如果我只是想在過去的25輪在傳遞給method_stats查詢狀態我稱之爲:

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 

同樣,我剛添加的限制,爲了子句來查詢。適用於所有記錄。

如果我模擬與出控制檯程序使用上述方法(或使用它們!)我會得到一個錯誤的總和

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 

不知道我發現了一個bug在AREL或我做錯了什麼。我試着用Round來代替STI ScoredRound,結果相同。

任何想法?

回答

1

如果你注意到,在SUM結果兩者有和沒有LIMIT,都是一樣的:

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.pluck(:quality).sum的情況,它只適用於查詢返回的25條記錄。

不知道,如果我發現AREL一個bug或我做錯了什麼

可悲的是,次99.9%是不是一個錯誤,但我們的錯:(

+0

感謝解釋。我實際上發現一個問題關於同一個問題的一年以前的帖子,以及解釋限制不是查詢的一部分[ActiveRecord對子集或記錄的計算](http://stackoverflow.com/questions/37020161/ActiveRecord的-計算-上的子集或 - 記錄)。 – appleII717

0
# 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)再總結采取質量作爲列名,並計算在給定的列值的總和。

相關問題