2010-11-24 1 views
1

Der Versuch, mit Rails 2.3 und MySQL für einige Berichte möglichst effiziente Daten zu erhalten.Rails - Effizientes Ziehen und Berechnen von Daten über mehrere Beziehungen hinweg

Unsere App verfügt über Benutzer und Deals und PurchasedDeals. Beziehungen wie folgt aussehen:

class User 
    has_many :purchased_deals 
    has_many :deals, :through => :purchased_deals 
end 

class Deal 
    has_many :purchased_deals 
    has_many :users, :through => :purchased_deals 
end 

class PurchasedDeal 
    belongs_to :deal 
    belongs_to :user 
end 

Für den Bericht Ich laufe, ich brauche alle Benutzer zu erhalten, die einen Kauf getätigt haben (dh mindestens eine PurchasedDeal) und dann die Summe aller Angebote, die sie gekauft haben (der Preis ist dem Deal beigefügt, nicht der PurchasedDeal).

Natürlich könnte ich mit einer Liste aller Benutzer beginnen, einschließlich sowohl Deals und gekauften Deals. Ich habe das versucht, und die Abfrage ist massiv (30.000 Benutzer geben oder nehmen, 3.000 Angebote, 100.000 gekaufte Angebote).

ich mit Benutzern beginnen könnte, dann ein .each tun und diejenigen finden, die eine gekaufte Geschäft haben, teilen sie in ihre eigene Gruppe, und dann über die jeweils jene iterieren die gesamte gekaufte Menge zu bekommen, aber Das ist eine Menge Anfragen.

Derzeit dauern beide Methoden so lange, dass die Anforderungen überschritten werden. Was wäre der effizienteste Weg, um die Daten zu bekommen, die ich brauche? Das Hinzufügen von Spalten zu Tabellen ist eine völlig akzeptable Lösung, zwischenzeitlich. Ich habe vollen Datenbankzugriff, um zu tun, was ich brauche.

Danke!

Antwort

0

Um eine Liste von Benutzer-IDs mit mehr als einem Kauf zu erhalten, können Sie das tun, folgende, die nur eine Tabelle zugreifen:

user_ids = PurchasedDeal.count(:group => :user_id, :having => 'count_all > 0').keys 

Anschließend können Sie alle diese Benutzer holen mit:

users = User.find user_ids 

Dinge können mit einem Zähler-Cache beschleunigt werden. Fügen Sie in Ihrem Benutzermodell die Option :counter_cache => true der has_many Assoziation für gekaufte Deals hinzu. Sie erhalten eine zusätzliche Integer-Spalte auf der Benutzer-Tabelle müssen und zu initialisieren, die in einer Migration wie folgt aussehen könnte:

add_column :users, :purchased_deals_count, :integer, :null => false, :default => 0 
User.each { |u| User.reset_counters u, :purchased_deals } 

Sobald das ist aus dem Weg, wird es viel einfacher:

users = User.all :conditions => 'purchased_deals_count > 0' 

Schienen halten die Säule bei den meisten Standardoperationen für Sie auf dem neuesten Stand.


Um den Gesamtpreis zu erhalten, wird immer ein Beitritt mit einbeziehen. Oder Sie können ein Hash von Deal-Preisen erstellen und die langwierige Verarbeitung in Ruby durchführen. Ich bin kein SQL-Experte, aber Sie können den Join möglicherweise umgehen, indem Sie den Preis bei PurchasedDeal speichern. Ansonsten ist hier, wie es mit einem Join zu tun:

user_id_to_price = PurchasedDeal.sum 'deal.price', :include => :deal, :group => :user_id 

Sie konnten das Filter nur auf die Benutzer, die Sie von so etwas wie :conditions => ['user_id IN (?)', users] Hinzufügen möchten.(users kann eine Liste von IDs sein, aber auch Benutzerobjekte.)

+0

Wenn ich versuche, Ihre erste Anweisung auszuführen, wird ein mysql-Fehler zurückgegeben, da "count_all" eine unbekannte Spalte ist. –

+0

Mein schlechtes, das hätte sein sollen: "haben", anstatt "Bedingungen". Fest. –

+0

Wie würde ich user_id_to_price sortieren/nachbestellen? die Art! Methode gibt mir keinen Methodenfehler (es ist ein OrderedHash). –

0

Sie fügen Preisspalte Angenommen Tabelle purchased_deals Sie Preisinformationen von Benutzern und Gesamt Angebote wie diese bekommen konnte:

select users.id, sum(purchased_deals.price) from users, purchased_deals where users.id = purchased_deals.user_id group by users.id having sum(purchased_deals.price) > 0 
Verwandte Themen