ActiveRecord: tamanho vs contagem

201

No Rails, você pode encontrar o número de registros usando ambos Model.sizee Model.count. Se você estiver lidando com consultas mais complexas, existe alguma vantagem em usar um método em detrimento do outro? Como eles são diferentes?

Por exemplo, tenho usuários com fotos. Se eu quiser mostrar uma tabela de usuários e quantas fotos eles tiverem, a execução de muitas instâncias user.photos.sizeserá mais rápida ou mais lenta do que user.photos.count?

Obrigado!

Andrew
fonte

Respostas:

344

Você deve ler isso , ainda é válido.

Você adaptará a função que usar, dependendo de suas necessidades.

Basicamente:

  • se você já carrega todas as entradas, digamos User.all, você deve usar lengthpara evitar outra consulta db

  • se você não tiver carregado nada, use countpara fazer uma consulta de contagem no seu banco de dados

  • se você não quiser se preocupar com essas considerações, use o sizeque irá adaptar

apneadiving
fonte
35
Se sizeadapta à situação de qualquer maneira, então qual é a necessidade para lengthe countem tudo?
Sscirrus 21/05
27
@sscirus - Para que você sizepossa fazer uma ligação quando você fizer a ligação para size(depois que ela determinar para qual ligação).
Batkins
35
Entretanto, tenha cuidado apenas com o tamanho padrão. Por exemplo, se você criar um novo registro sem passar pela relação, ou seja Comment.create(post_id: post.id), você post.comments.sizenão estará atualizado, enquanto post.comments.countestará. Portanto, tenha cuidado.
Mrbrdo 31/03
14
Além disso, se você criar vários objetos por meio de uma relação company.devices.build(:name => "device1"); company.devices.build(:name => "device2"):, company.devices.sizee .lengthincluirá o número de objetos que você criou, mas não salvou, .countrelatará apenas a contagem do banco de dados.
Shawn J. Goff
6
@sscirrus, size é um comando perigoso, pois é automatizado, às vezes você deseja consultar o banco de dados novamente.
Alex C
79

Como as outras respostas afirmam:

  • countirá executar uma COUNTconsulta SQL
  • length irá calcular o comprimento da matriz resultante
  • size tentará escolher o mais apropriado para evitar consultas excessivas

Mas há mais uma coisa. Percebemos um caso em que sizeatua de maneira diferente count/ lengthe pensei em compartilhá-lo, pois é raro o suficiente para ser esquecido.

  • Se você usar um :counter_cacheem uma has_manyassociação, sizeusará a contagem em cache diretamente e não fará uma consulta extra.

    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

Esse comportamento está documentado nos Guias do Rails , mas eu o perdi pela primeira vez ou o esqueci.

Lima
fonte
De fato, antes do rails 5.0.0.beta1, esse comportamento seria acionado mesmo se houvesse uma _countcoluna (sem a counter_cache: truediretiva sobre a associação). Isso foi corrigido no github.com/rails/rails/commit/e0cb21f5f7
cbliard
8

Às vezes size"escolhe o errado" e retorna um hash (que é o countque faria)

Nesse caso, use lengthpara obter um número inteiro em vez de hash .

jvalanen
fonte
Eu usei '.size' em uma coleção de uma instância has_many e, mesmo que houvesse um registro na coleção, o tamanho estava retornando um '0'. Usando .count retornou o valor correto de '1'.
admazzola
4

tl; dr

  • Se você sabe que não precisará do uso de dados count.
  • Se você sabe que irá usar ou usou os dados length.
  • Se você não sabe o que está fazendo, use size...

contagem

Resolve enviar uma Select count(*)...consulta ao banco de dados. O caminho a percorrer se você não precisar dos dados, mas apenas a contagem.

Exemplo: contagem de novas mensagens, total de elementos quando apenas uma página será exibida etc.

comprimento

Carrega os dados necessários, ou seja, a consulta conforme necessário e, em seguida, apenas conta. O caminho a percorrer se você estiver usando os dados.

Exemplo: Resumo de uma tabela totalmente carregada, títulos dos dados exibidos etc.

Tamanho

Ele verifica se os dados foram carregados (ou seja, já nos trilhos), se sim, e apenas conte, caso contrário, ele chama count. (mais as armadilhas, já mencionadas em outras entradas).

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

Qual é o problema?

Que você pode estar acessando o banco de dados duas vezes se não o fizer na ordem correta (por exemplo, se você renderizar o número de elementos em uma tabela em cima da tabela renderizada, efetivamente haverá 2 chamadas enviadas ao banco de dados).

estani
fonte
3

Todas as estratégias a seguir fazem uma chamada ao banco de dados para executar uma COUNT(*)consulta.

Model.count

Model.all.size

records = Model.all
records.count

O seguinte não é tão eficiente quanto ele carregará todos os registros do banco de dados no Ruby, que conta o tamanho da coleção.

records = Model.all
records.size

Se seus modelos têm associações e você deseja encontrar o número de objetos pertencentes (por exemplo @customer.orders.size), é possível evitar consultas ao banco de dados (leituras de disco). Use um cache de contador e o Rails manterá o valor do cache atualizado e retornará esse valor em resposta ao sizemétodo.

Dennis
fonte
2
Ambos Model.all.sizee Model.all.countgeram uma countconsulta no Rails 4 e acima. A vantagem real sizeé que ela não gera a consulta de contagem se a associação já estiver carregada. No Rails 3 e abaixo, acredito que Model.allnão seja uma relação, portanto todos os registros já estão carregados. Esta resposta pode estar desatualizada e sugiro excluí-la.
31417 Damon Aw
1

Eu recomendo usar a função de tamanho.

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

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

Considere estes dois modelos. O cliente possui muitas atividades.

Se você usar um: counter_cache em uma associação has_many, o tamanho usará a contagem em cache diretamente e não fará uma consulta extra.

Considere um exemplo: no meu banco de dados, um cliente possui 20.000 atividades e tento contar o número de registros de atividades desse cliente com cada método de contagem, comprimento e tamanho. aqui abaixo do relatório de referência de todos esses 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)

então descobri que usar: counter_cache Size é a melhor opção para calcular o número de registros.

manthan andharia
fonte