ActiveRecord: dimensione vs conteggio


201

In Rails puoi trovare il numero di record usando sia Model.sizee Model.count. Se hai a che fare con query più complesse, c'è qualche vantaggio nell'usare un metodo rispetto all'altro? Come sono differenti?

Ad esempio, ho utenti con foto. Se voglio mostrare una tabella di utenti e quante foto hanno, l'esecuzione di molte istanze user.photos.sizesarà più veloce o più lenta di user.photos.count?

Grazie!

Risposte:


344

Si consiglia di leggere che , è ancora valido.

Adatterai la funzione che usi in base alle tue esigenze.

Fondamentalmente:

  • se si caricano già tutte le voci, ad esempio User.all, è necessario utilizzare lengthper evitare un'altra query db

  • se non hai caricato nulla, usa countper fare una query di conteggio sul tuo db

  • se non vuoi preoccuparti di queste considerazioni, usa sizequale si adatterà


35
Se si sizeadatta comunque alla situazione, allora che bisogno c'è lengthe countper niente?
sscirrus,

27
@sscirus - In modo che sizepossano effettuare una chiamata quando si effettua la chiamata size(dopo che determina quale chiamare).
Batkins,

35
Tuttavia, fai attenzione alle dimensioni predefinite. Ad esempio, se crei un nuovo record senza passare attraverso la relazione, ad esempio Comment.create(post_id: post.id), post.comments.sizenon sarai aggiornato, mentre lo post.comments.countsarà. Quindi stai solo attento.
mrbrdo,

14
Inoltre, se si costruisce diversi oggetti attraverso una relazione: company.devices.build(:name => "device1"); company.devices.build(:name => "device2"), quindi company.devices.sizee .lengthincluderà il numero di oggetti che hai costruito, ma non sono stati salvati, .countriferirà solo il conteggio dal database.
Shawn J. Goff,

6
@sscirrus, size è un comando pericoloso poiché è automatizzato, a volte si desidera interrogare nuovamente il db.
Alex C

79

Come affermano le altre risposte:

  • counteseguirà una COUNTquery SQL
  • length calcolerà la lunghezza dell'array risultante
  • size proverà a scegliere il più appropriato dei due per evitare query eccessive

Ma c'è un'altra cosa. Abbiamo notato un caso in cui sizeagisce diversamente count/ del lengthtutto, e ho pensato di condividerlo dal momento che è abbastanza raro da essere trascurato.

  • Se si utilizza a :counter_cachesu has_manyun'associazione, sizeverrà utilizzato direttamente il conteggio memorizzato nella cache e non verrà eseguita alcuna query aggiuntiva.

    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

Questo comportamento è documentato nelle Guide di Rails , ma l'ho perso la prima volta o me ne sono dimenticato.


In effetti, prima di rails 5.0.0.beta1, questo comportamento sarebbe stato attivato anche se fosse presente una _countcolonna (senza la counter_cache: truedirettiva sull'associazione). Questo
problema

8

A volte size"sceglie quello sbagliato" e restituisce un hash (che è ciò che countfarebbe)

In tal caso, utilizzare lengthper ottenere un numero intero anziché l' hash .


Ho usato '.size' su una raccolta da un'istanza has_many e anche se c'era un record nella raccolta, la dimensione restituiva uno '0'. L'uso di .count ha restituito il valore corretto di '1'.
admazzola,

4

tl; dr

  • Se sai che non avrai bisogno dell'uso dei dati count.
  • Se sai che userai o hai usato i dati length.
  • Se non sai cosa stai facendo, usa size...

contare

Risolve all'invio di una Select count(*)...query al DB. La strada da percorrere se non hai bisogno dei dati, ma solo del conteggio.

Esempio: conteggio di nuovi messaggi, elementi totali quando verrà visualizzata solo una pagina, ecc.

lunghezza

Carica i dati richiesti, ovvero la query come richiesto, quindi li conta. La strada da percorrere se si utilizzano i dati.

Esempio: riepilogo di una tabella completamente caricata, titoli dei dati visualizzati, ecc.

taglia

Verifica se i dati sono stati caricati (ovvero già nei binari) in tal caso, quindi contali, altrimenti chiama il conteggio. (oltre alle insidie, già menzionate in altre voci).

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

Qual è il problema?

Che potresti colpire il DB due volte se non lo fai nel giusto ordine (ad es. Se esegui il rendering del numero di elementi in una tabella sopra la tabella renderizzata, verranno effettivamente inviate 2 chiamate al DB).


3

Le seguenti strategie effettuano tutte una chiamata al database per eseguire una COUNT(*)query.

Model.count

Model.all.size

records = Model.all
records.count

Quanto segue non è efficiente in quanto caricherà tutti i record dal database in Ruby, che quindi conta le dimensioni della raccolta.

records = Model.all
records.size

Se i tuoi modelli hanno associazioni e desideri trovare il numero di oggetti appartenenti (ad es. @customer.orders.size), Puoi evitare le query del database (letture del disco). Utilizzare una cache contatore e Rails manterrà aggiornato il valore della cache e restituirà quel valore in risposta al sizemetodo.


2
Entrambi Model.all.sizee Model.all.countgenerano una countquery in Rails 4 e versioni successive. Il vero vantaggio sizeè che non genera la query di conteggio se l'associazione è già caricata. In Rails 3 e versioni precedenti, credo che Model.allnon sia una relazione, quindi tutti i record sono già caricati. Questa risposta potrebbe non essere aggiornata e ti suggerisco di eliminarla.
Damon Aw,

1

Ho raccomandato di utilizzare la funzione di dimensione.

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

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

Considera questi due modelli. Il cliente ha molte attività del cliente.

Se si utilizza a: counter_cache in un'associazione has_many, size utilizzerà direttamente il conteggio memorizzato nella cache e non eseguirà alcuna query aggiuntiva.

Considera un esempio: nel mio database, un cliente ha 20.000 attività del cliente e provo a contare il numero di record delle attività del cliente di quel cliente con ciascuno dei metodi di conteggio, lunghezza e dimensione. qui sotto il rapporto di riferimento di tutti questi metodi.

            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)

così ho scoperto che usando: counter_cache Size è l'opzione migliore per calcolare il numero di record.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.