ActiveRecord: Größe gegen Anzahl

201

In Rails finden Sie die Anzahl der Datensätze mit Model.sizeund Model.count. Wenn Sie mit komplexeren Abfragen arbeiten, hat die Verwendung einer Methode gegenüber der anderen einen Vorteil? Wie unterscheiden sie sich?

Zum Beispiel habe ich Benutzer mit Fotos. Wenn ich eine Tabelle mit Benutzern anzeigen möchte und wie viele Fotos sie haben, werden viele Instanzen user.photos.sizeschneller oder langsamer ausgeführt als user.photos.count?

Vielen Dank!

Andrew
quelle

Antworten:

344

Sie sollten lesen , dass , es ist immer noch gültig.

Sie passen die von Ihnen verwendete Funktion an Ihre Bedürfnisse an.

Grundsätzlich:

  • Wenn Sie beispielsweise bereits alle Einträge geladen haben, User.allsollten Sie diese verwenden length, um eine weitere Datenbankabfrage zu vermeiden

  • Wenn Sie nichts geladen haben, verwenden Sie count, um eine Zählabfrage für Ihre Datenbank durchzuführen

  • Wenn Sie sich nicht mit diesen Überlegungen beschäftigen möchten, verwenden Sie diese, um sizesie anzupassen

apneadiving
quelle
35
Wenn sizepasst sich an die Situation sowieso, was dann notwendig ist , da für lengthund countüberhaupt?
sscirrus
27
@sscirus - Damit sizekönnen sie angerufen werden, wenn Sie den Anruf tätigen size(nachdem festgelegt wurde, welcher angerufen werden soll).
Batkins
35
Seien Sie jedoch vorsichtig, wenn Sie nur die Größe als Standard festlegen. Wenn Sie beispielsweise einen neuen Datensatz erstellen, ohne die Beziehung zu durchlaufen, Comment.create(post_id: post.id)ist Ihr Datensatz post.comments.sizenicht auf dem neuesten Stand post.comments.count. Also sei einfach vorsichtig.
Mrbrdo
14
Wenn Sie mehrere Objekte über eine Beziehung erstellen : company.devices.build(:name => "device1"); company.devices.build(:name => "device2"), company.devices.sizeund .lengthdie Anzahl der Objekte enthält, die Sie erstellt, aber nicht gespeichert haben, .countwird nur die Anzahl aus der Datenbank gemeldet.
Shawn J. Goff
6
@sscirrus, Größe ist ein gefährlicher Befehl, da er automatisiert ist. Manchmal möchten Sie die Datenbank erneut abfragen.
Alex C
79

In den anderen Antworten heißt es:

  • countführt eine SQL- COUNTAbfrage durch
  • length berechnet die Länge des resultierenden Arrays
  • size wird versuchen, die am besten geeignete der beiden auszuwählen, um übermäßige Abfragen zu vermeiden

Aber es gibt noch eine Sache. Wir bemerkten einen Fall , wo sizeanders wirkt auf count/ lengthzusammen, und ich dachte ich , es würde teilen , da es selten genug ist , übersehen zu werden.

  • Wenn Sie a :counter_cachefür eine has_manyZuordnung verwenden, sizewird die zwischengespeicherte Anzahl direkt verwendet und es wird überhaupt keine zusätzliche Abfrage durchgeführt.

    class Image < ActiveRecord::Base
      belongs_to :product, counter_cache: true
    end
    
    class Product < ActiveRecord::Base
      has_many :images
    end
    
    > product = Product.first  # query, load product into memory
    > product.images.size      # no query, reads the :images_count column
    > product.images.count     # query, SQL COUNT
    > product.images.length    # query, loads images into memory

Dieses Verhalten ist in den Rails Guides dokumentiert , aber ich habe es entweder beim ersten Mal verpasst oder vergessen.

Limette
quelle
Tatsächlich würde dieses Verhalten vor Rails 5.0.0.beta1 ausgelöst, selbst wenn eine _countSpalte vorhanden ist (ohne die counter_cache: trueAnweisung zur Zuordnung). Dies wurde in github.com/rails/rails/commit/e0cb21f5f7
cbliard
8

Manchmal size"wählt den falschen aus" und gibt einen Hash zurück (was counttun würde)

Verwenden Sie lengthin diesem Fall, um eine Ganzzahl anstelle von Hash abzurufen .

jvalanen
quelle
Ich habe '.size' für eine Sammlung aus einer has_many-Instanz verwendet, und obwohl die Sammlung einen Datensatz enthielt, gab size eine '0' zurück. Bei Verwendung von .count wurde der korrekte Wert '1' zurückgegeben.
Admazzola
4

tl; dr

  • Wenn Sie wissen, dass Sie die Datennutzung nicht benötigen count.
  • Wenn Sie wissen, dass Sie die Datennutzung verwenden oder verwendet haben length.
  • Wenn Sie nicht wissen, was Sie tun, verwenden Sie size...

Anzahl

Beschließt, eine Select count(*)...Abfrage an die Datenbank zu senden . Der richtige Weg, wenn Sie nicht die Daten, sondern nur die Anzahl benötigen.

Beispiel: Anzahl neuer Nachrichten, Gesamtzahl der Elemente, wenn nur eine Seite angezeigt werden soll usw.

Länge

Lädt die erforderlichen Daten, dh die Abfrage nach Bedarf, und zählt sie dann einfach. Der richtige Weg, wenn Sie die Daten verwenden.

Beispiel: Zusammenfassung einer vollständig geladenen Tabelle, Titel der angezeigten Daten usw.

Größe

Es prüft, ob die Daten geladen wurden (dh bereits in Schienen), wenn dies der Fall ist, und zählt sie dann einfach, andernfalls wird count aufgerufen. (plus die Fallstricke, die bereits in anderen Einträgen erwähnt wurden).

def size
  loaded? ? @records.length : count(:all)
end

Was ist das Problem?

Dass Sie die Datenbank möglicherweise zweimal treffen, wenn Sie dies nicht in der richtigen Reihenfolge tun (z. B. wenn Sie die Anzahl der Elemente in einer Tabelle über der gerenderten Tabelle rendern, werden effektiv 2 Aufrufe an die Datenbank gesendet).

estani
quelle
3

Die folgenden Strategien rufen alle die Datenbank auf, um eine COUNT(*)Abfrage durchzuführen .

Model.count

Model.all.size

records = Model.all
records.count

Das Folgende ist nicht so effizient, da alle Datensätze aus der Datenbank in Ruby geladen werden, das dann die Größe der Sammlung zählt.

records = Model.all
records.size

Wenn Ihre Modelle Assoziationen haben und Sie die Anzahl der zugehörigen Objekte ermitteln möchten (z. B. @customer.orders.size), können Sie Datenbankabfragen (Festplattenlesevorgänge) vermeiden. Verwenden Sie einen Zähler-Cache, und Rails hält den Cache-Wert auf dem neuesten Stand und gibt diesen Wert als Antwort auf die sizeMethode zurück.

Dennis
quelle
2
Beides Model.all.sizeund Model.all.countgenerieren eine countAbfrage in Rails 4 und höher. Der eigentliche Vorteil von sizeist, dass die Zählabfrage nicht generiert wird, wenn die Zuordnung bereits geladen ist. In Rails 3 und darunter glaube ich, dass Model.alles sich nicht um eine Beziehung handelt, daher sind alle Datensätze bereits geladen. Diese Antwort ist möglicherweise veraltet und ich schlage vor, sie zu löschen.
Damon Aw
1

Ich habe empfohlen, die Größenfunktion zu verwenden.

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

class CustomerActivity < ActiveRecord::Base
  belongs_to :customer, counter_cache: true
end

Betrachten Sie diese beiden Modelle. Der Kunde hat viele Kundenaktivitäten.

Wenn Sie einen: counter_cache für eine has_many-Zuordnung verwenden, verwendet size die zwischengespeicherte Anzahl direkt und führt überhaupt keine zusätzliche Abfrage durch.

Betrachten Sie ein Beispiel: In meiner Datenbank hat ein Kunde 20.000 Kundenaktivitäten und ich versuche, die Anzahl der Datensätze der Kundenaktivitäten dieses Kunden mit jeder Zähl-, Längen- und Größenmethode zu zählen. hier unten der Benchmark-Bericht aller dieser Methoden.

            user     system      total        real
Count:     0.000000   0.000000   0.000000 (  0.006105)
Size:      0.010000   0.000000   0.010000 (  0.003797)
Length:    0.030000   0.000000   0.030000 (  0.026481)

Daher fand ich, dass die Verwendung von: counter_cache Size die beste Option ist, um die Anzahl der Datensätze zu berechnen.

Manthan Andharia
quelle