2009-06-11 5 views
10

Wie kann ich eine AR Fund Abfrage schreiben sortieren die Ergebnisse durch die Anzahl der Datensätze in einem has_many Verein bestellt hat?Wie Rails AR.find nach Anzahl der Objekte in einer has_many Beziehung

class User < ActiveRecord::Base 
    has_many :photos 
end 

Ich möchte wie etwas tun ...

User.find(:all, :order => photos.count) 

Ich weiß, meine Entdeckung nicht gültigen Code ist. Sagen wir, ich habe folgende Daten.

User 1, which has 3 photos 
User 2, which has 5 photos 
User 3, which has 2 photos 

Ich möchte meine find mich über die Benutzer in der Reihenfolge bringen ...

User 2, 
User 1, 
User 3 

basierend auf der Anzahl der der Nutzer Fotos

Antwort

13

Wenn Sie nicht über eine zusätzliche Spalte möchten, könnten Sie immer für eine zusätzliche Spalte in der zurückgegebenen Ergebnismenge fragen:

User.all(:select => "#{User.table_name}.*, COUNT(#{Photo.table_name}.id) number_of_photos", 
     :joins => :photos, 
     :order => "number_of_photos") 

Dies erzeugt die folgende SQL:

SELECT users.*, COUNT(photos.id) number_of_photos 
FROM `users` INNER JOIN `photos` ON photos.user_id = users.id 
ORDER BY number_of_photos 
+0

Das wirklich, was ich suchte Ich erklärte es einfach nicht gut. –

+0

+1 Für die Verwendung von Model.table_name, -1 für die Verwendung von: joins => "# {Photo.table_name.pluralize}"! –

+0

-1 für eifriges Laden aller, -1 für das Schreiben Ihrer eigenen SQL-Zählung, dass jeder MySQL verwendet. – ChuckE

1

Counter cache helfen, aber Sie brauche eine zusätzliche Spalte in der db.

-4

Ihre Frage ergibt keinen Sinn. Der Parameter :order gibt einen Spaltennamen und eine optionale Reihenfolge an, d. H. Asc (ending) oder desc (ending).

Was ist das Ergebnis, das Sie erreichen möchten?

+0

Richtig, merke ich, mein Fund ist kein gültiger Code. Angenommen, ich habe die folgenden Daten. Benutzer 1, die 3 Fotos Benutzer 2 hat, die 5 Fotos hat User 3, die 2 Fotos hat ich find wollen, dass ich die Nutzer in der Größenordnung von ... 2 Benutzer zurück zu bringen, Benutzer 1, Benutzer 3 basierend auf der Anzahl der Benutzer Fotos. –

+0

Er versucht, Benutzer nach der Summe ihrer Fotos zu sortieren –

19

Der einfachste Weg, dies zu erreichen, ist wahrscheinlich einen Zähler Cache zu diesem Modell hinzuzufügen und dann sortieren nach dieser Spalte.

class Photo < ActiveRecord::Base 
    belongs_to :user, :counter_cache => true 
end 

Und sicher sein, eine Spalte zu Ihrer users Tabelle photos_count genannt hinzuzufügen.

Dann werden Sie in der Lage zu ...

User.find(:all, :order => 'photos_count') 
7

Wenn Sie nicht über einen Zähler Cache Spalte, Ihre einzige Option hinzufügen möchten, ist nach dem Fund zu sortieren. Wenn Sie die Assoziation in Ihrer Suche :include finden, entstehen Ihnen keine zusätzlichen Datenbankarbeiten.

users = User.find(:all, :include => :photos).sort_by { |u| -u.photos.size } 

Hinweis das negative Vorzeichen im sort_by Block von hoch zu niedrig zu sortieren.

+1

Ich glaube, das Abrufen und nur das Sortieren von Ruby-Objekten ist dann viel langsamer als das Vorsortieren auf DB-Ebene. – gmile

+0

Was schneller ist, hängt von der spezifischen Situation ab. Auf einem sehr ausgelasteten Datenbankserver könnte das Laden der Sortierung auf den Anwendungsserver (unter der Annahme einer anderen Hardware) schneller sein. In diesem Fall haben Sie Recht, weil meine Lösung die Fotosätze einliest. Das Vermeiden des ': include' und das Ausführen der Sortierung in Ruby würde zusätzliche Abfragen nach sich ziehen, was noch schlimmer ist. François 'akzeptierte Antwort zeigt, wie man innerhalb der Abfrage sortiert, ohne die Fotos einzuziehen, was mit ziemlicher Sicherheit die beste Lösung für diesen Fall ist. –

0

I‘ Ich füge das als Kommentar zur Top-Antwort hinzu, aber ich kann das aus irgendeinem Grund nicht tun. Nach diesem Beitrag:

http://m.onkey.org/active-record-query-interface

Die User.all(options) Methode wird nach Rails 3.0.3 und mit einer Reihe von anderen (praktisch, verkettbar) aktiv Satzart Material ersetzt veraltet, aber es macht es sehr schwer, finde heraus, wie man dieselbe Art von Abfrage zusammenstellt.

Als Ergebnis habe ich die Cache-Methode implementiert.Dies war ziemlich einfach und schmerzlos mit der Ausnahme, dass Sie daran denken müssen, die Spalteninformationen in Ihrer Migration zu aktualisieren, andernfalls haben alle vorhandenen Datensätze "0".

Hier ist, was ich in meiner Migration verwendet:

class AddUserCountToCollections < ActiveRecord::Migration 
    def self.up 
    add_column :collections, :collectionusers_count, :integer, :default => 0 
    Collection.reset_column_information 
    Collection.all.each do |c| 
     Collection.update_counters c.id, :collectionusers_count => c.collectionusers.count 
    end 
    end 

    def self.down 
    remove_column :collections, :collectionusers_count 
    end 
end 

Theoretisch dies schneller sein sollte, auch. Hoffe, das ist hilfreich für die Zukunft.

+0

kann auch reset_counters anstelle von update_counters verwenden – MatthewFord

5

Ich würde Ihnen raten, nicht direkt SQL zu schreiben, da Implementierungen davon von Geschäft zu Geschäft variieren können. Glücklicherweise haben Sie arel:

User.joins(:photos).group(Photo.arel_table[:user_id]). 
    order(Photo.arel_table[:user_id].count) 
+0

Warum finanzieren wir das nicht? –

+0

Kennen Sie die Sortierrichtung auch so, dass sie aufsteigend oder absteigend ist? – manveru

Verwandte Themen