ActiveRecord: tamaño vs conteo

201

En Rails, puede encontrar el número de registros utilizando ambos Model.sizey Model.count. Si se trata de consultas más complejas, ¿hay alguna ventaja en usar un método sobre el otro? ¿En qué se diferencian?

Por ejemplo, tengo usuarios con fotos. Si quiero mostrar una tabla de usuarios y cuántas fotos tienen, ¿se ejecutarán muchas instancias user.photos.sizemás rápido o más lento que user.photos.count?

¡Gracias!

Andrés
fuente

Respuestas:

344

Deberías leer eso , todavía es válido.

Adaptará la función que usa según sus necesidades.

Básicamente:

  • si ya carga todas las entradas, por ejemplo User.all, debe usar lengthpara evitar otra consulta de base de datos

  • si no tiene nada cargado, use countpara hacer una consulta de conteo en su base de datos

  • si no quiere molestarse con estas consideraciones, use el sizeque se adaptará

apneadiving
fuente
35
Si sizede todos modos se adapta a la situación, ¿qué necesidad hay para lengthy counten absoluto?
sscirrus
27
@sscirus: para que sizepueda hacer una llamada a ellos cuando realiza la llamada size(después de que determina a quién llamar).
Batkins
35
Sin embargo, tenga cuidado simplemente con el tamaño predeterminado. Por ejemplo, si crea un nuevo registro sin pasar por la relación, es decir Comment.create(post_id: post.id), su post.comments.sizeno estará actualizado, mientras que lo post.comments.counthará. Así que ten cuidado.
Mrbrdo
14
Además, si se construye varios objetos a través de una relación: company.devices.build(:name => "device1"); company.devices.build(:name => "device2")y, a continuación company.devices.sizee .lengthincluirá el número de objetos que se ha construido, pero no ha guardado, .countinformará sólo el recuento de la base de datos.
Shawn J. Goff
66
@sscirrus, el tamaño es un comando peligroso ya que está automatizado, a veces desea consultar el db nuevamente.
Alex C
79

Como dicen las otras respuestas:

  • countrealizará una COUNTconsulta SQL
  • length calculará la longitud de la matriz resultante
  • size intentará elegir el más apropiado de los dos para evitar consultas excesivas

Pero hay una cosa más. Notamos un caso en el que sizeactúa de manera diferente a count/ en lengthconjunto, y pensé en compartirlo ya que es bastante raro que se pase por alto.

  • Si usa un :counter_cacheen una has_manyasociación, sizeusará el recuento en caché directamente y no realizará ninguna consulta adicional.

    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

Este comportamiento está documentado en las Guías de Rails , pero lo perdí la primera vez o lo olvidé.

Lima
fuente
De hecho, antes de rails 5.0.0.beta1, este comportamiento se desencadenaría incluso si hay una _countcolumna (sin la counter_cache: truedirectiva sobre la asociación). Esto se ha solucionado en github.com/rails/rails/commit/e0cb21f5f7
cbliard
8

A veces size"elige el incorrecto" y devuelve un hash (que es lo countque haría)

En ese caso, use lengthpara obtener un número entero en lugar de hash .

jvalanen
fuente
Usé '.size' en una Colección de una instancia has_many y, aunque había un registro en la colección, el tamaño devolvía un '0'. El uso de .count devolvió el valor correcto de '1'.
admazzola
4

tl; dr

  • Si sabe que no necesitará el uso de datos count.
  • Si sabe que usará o habrá usado el uso de datos length.
  • Si no sabe lo que está haciendo, use size...

contar

Resuelve enviar una Select count(*)...consulta a la base de datos. El camino a seguir si no necesita los datos, sino solo el recuento.

Ejemplo: recuento de mensajes nuevos, elementos totales cuando solo se va a mostrar una página, etc.

longitud

Carga los datos requeridos, es decir, la consulta según sea necesario, y luego solo los cuenta. El camino a seguir si está utilizando los datos.

Ejemplo: resumen de una tabla completamente cargada, títulos de datos mostrados, etc.

Talla

Comprueba si los datos se cargaron (es decir, que ya están en rieles), de ser así, simplemente cuéntelos, de lo contrario, llama a contar. (más las trampas, ya mencionadas en otras entradas).

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

¿Cuál es el problema?

Es posible que esté golpeando la base de datos dos veces si no lo hace en el orden correcto (por ejemplo, si representa el número de elementos en una tabla encima de la tabla representada, efectivamente se enviarán 2 llamadas a la base de datos).

estani
fuente
3

Todas las siguientes estrategias hacen una llamada a la base de datos para realizar una COUNT(*)consulta.

Model.count

Model.all.size

records = Model.all
records.count

Lo siguiente no es tan eficiente, ya que cargará todos los registros de la base de datos en Ruby, que luego cuenta el tamaño de la colección.

records = Model.all
records.size

Si sus modelos tienen asociaciones y desea encontrar el número de objetos pertenecientes (p @customer.orders.size. Ej. ), Puede evitar consultas en la base de datos (lecturas de disco). Utilice un caché de contador y Rails mantendrá el valor de caché actualizado y devolverá ese valor en respuesta al sizemétodo.

Dennis
fuente
2
Ambos Model.all.sizey Model.all.countgeneran una countconsulta en Rails 4 y superiores. La verdadera ventaja de sizees que no genera la consulta de conteo si la asociación ya está cargada. En Rails 3 y versiones posteriores, creo Model.allque no es una relación, por lo tanto, todos los registros ya están cargados. Esta respuesta puede estar desactualizada y sugiero eliminarla.
Damon Aw
1

Recomiendo usar la función de tamaño.

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

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

Considere estos dos modelos. El cliente tiene muchas actividades de clientes.

Si usa un: counter_cache en una asociación has_many, size usará el recuento en caché directamente y no realizará ninguna consulta adicional.

Considere un ejemplo: en mi base de datos, un cliente tiene 20,000 actividades de clientes e intento contar el número de registros de actividades de clientes de ese cliente con cada método de conteo, longitud y tamaño. aquí debajo el informe de referencia de todos estos métodos.

            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)

así que descubrí que usar: counter_cache Size es la mejor opción para calcular la cantidad de registros.

Manthan andharia
fuente