2017-05-13 2 views
1

Ich habe eine Methode, die Stats (hauptsächlich Summen) für eine Anzahl von Float-Attribute in einem Modell berechnet.Rails Summe auf AssociationRelation Attribut ist falsch, wenn die Assoziation limit Klausel

Die Modelle

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 

Die Methode, die Statistik auf float-Attribute bis zu 4 sieht vor, dass von einem anderen Methode aufgerufen wird, je nachdem, ob ich Statistiken für einen Spieler oder eine Gruppe von Spielern bekommen. Ein erster Filter auf ScoredRound wird an die Methode übergeben (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 

3 der 4 Attribute I in ScoredRounds bin Summieren kann nicht gesetzt ist (Null), wenn der Spieler nicht das Spiel gewonnen haben, so dass die Runden gefiltert werden .

Alles funktionierte gut, bis ich beschloss, ein Limit für die Anzahl der zu verwendenden Runden hinzuzufügen. Zum Beispiel, wenn ich nur Status für die letzten 25 Runden in der Abfrage übergeben method_stats wollte würde ich nennen:

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 

Auch hier habe ich nur noch die Grenze und um Klausel für die Abfrage. Funktionierte gut für alle Datensätze.

Wenn ich das Verfahren in der Konsole mit geführt unter Verwendung der oben genannten Methoden (oder mit ihnen!) Simulieren Ich werde eine fehlerhafte Summe erhalten

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 

Weiß nicht, ob ich einen Fehler in AREL gefunden oder Ich mache etwas falsch. Ich habe es mit nur Round anstelle der STI ScoredRound mit den gleichen Ergebnissen versucht.

Irgendwelche Ideen?

Antwort

1

Wenn Sie bemerken, die SUM Ergebnisse für beide, mit und ohne LIMIT, sind die gleichen:

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 

Das ist, weil LIMIT die Anzahl der Zeilen beeinflusst von der Abfrage zurückgegeben und SUM kehrt nur ein, so dass die Funktion wird für alle 44 Datensätze angewendet, nicht die 25, die an LIMIT übergeben werden. Das passiert nicht mit sr.pluck(:quality).sum, die nur für die 25 Datensätze gilt, die von der Abfrage zurückgegeben werden.

Weiß nicht, ob ich einen Fehler in AREL oder ich mache etwas falsch

, ist 99,9% der Zeit Leider fand kein Bug, sondern unser Fehler :(

+0

Vielen Dank für Ich fand tatsächlich einen einjährigen Beitrag, der nach der gleichen Frage fragte, und eine Antwort, die erklärte, dass das Limit nicht wirklich Teil der Abfrage ist [ActiveRecord Berechnungen auf Teilmengen oder Datensätzen] (http://stackoverflow.com/questions/37020161)/activerecord-Berechnungen-auf-Untermengen-oder-Aufzeichnungen). – 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 

wenn Sie sr.sum(:quality) dann rufen Qualität als Spaltenname nehmen summieren und berechnet die Summe der Werte auf einer bestimmten Spalte.

Verwandte Themen