ActiveRecord: размер против количества

201

В Rails вы можете найти количество записей, используя Model.sizeи Model.count. Если вы имеете дело с более сложными запросами, есть ли преимущество в использовании одного метода перед другим? Насколько они разные?

Например, у меня есть пользователи с фотографиями. Если я хочу показать таблицу пользователей и сколько у них фотографий, будет ли запускать много экземпляров user.photos.sizeбыстрее или медленнее, чем user.photos.count?

Спасибо!

Андрей
источник

Ответы:

344

Вы должны прочитать это , это все еще в силе.

Вы адаптируете функцию, которую используете, в зависимости от ваших потребностей.

В принципе:

  • скажем User.all, если вы уже загрузили все записи , вы должны использовать, lengthчтобы избежать другого запроса БД

  • если вы ничего не загрузили, используйте, countчтобы сделать запрос подсчета на вашей базе данных

  • если вы не хотите беспокоиться об этих соображениях, используйте sizeкоторый адаптирует

apneadiving
источник
35
Если sizeвсе равно адаптируется к ситуации, то зачем lengthи countвообще нужно ?
sscirrus
27
@sscirus - чтобы sizeможно было звонить им, когда вы звоните size(после того, как он определит, кому звонить).
Баткинс
35
Однако будьте осторожны, просто указав размер по умолчанию. Например, если вы создаете новую запись без прохождения отношения, т. Е. Comment.create(post_id: post.id)Ваша post.comments.sizeне будет актуальной, пока post.comments.countбудет. Так что просто будь осторожен.
mrbrdo
14
Кроме того, если вы построите несколько объектов с помощью отношения:, company.devices.build(:name => "device1"); company.devices.build(:name => "device2")тогда company.devices.sizeи .lengthбудет включать количество объектов, которые вы создали, но не сохранили, .countбудет сообщать только счетчик из базы данных.
Шон Дж. Гофф
6
@sscirrus, size - опасная команда, так как она автоматизирована, иногда вам хочется снова запросить базу данных.
Алекс С
79

Поскольку другие ответы заявляют:

  • countвыполнит COUNTзапрос SQL
  • length рассчитает длину полученного массива
  • size постараюсь выбрать наиболее подходящий из двух, чтобы избежать чрезмерных запросов

Но есть еще одна вещь. Мы заметили случай, когда sizeдействует иначе countили в lengthцелом, и я подумал, что поделюсь им, потому что это достаточно редко, чтобы его упустить из виду.

  • Если вы используете :counter_cacheна has_manyассоциацию, sizeбудет использовать кэшированную рассчитывать непосредственно, а не сделать дополнительный запрос вообще.

    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

Это поведение описано в Руководствах по Rails , но я либо пропустил его в первый раз, либо забыл об этом.

Лайм
источник
Фактически, до rails 5.0.0.beta1 такое поведение было бы запущено, даже если есть _countстолбец (без counter_cache: trueдирективы об ассоциации). Это было исправлено в github.com/rails/rails/commit/e0cb21f5f7
cbliard
8

Иногда size«выбирает неправильный» и возвращает хэш (что и countбудет)

В этом случае используйте, lengthчтобы получить целое число вместо хеша .

jvalanen
источник
Я использовал «.size» для коллекции из экземпляра has_many, и хотя в коллекции была одна запись, размер возвращал «0». Использование .count вернуло правильное значение «1».
Admazzola
4

ТЛ; др

  • Если вы знаете, что вам не понадобится использование данных count.
  • Если вы знаете, вы будете использовать или использовали данные использования length.
  • Если вы не знаете, что делаете, используйте size...

подсчитывать

Разрешает отправку Select count(*)...запроса в БД. Путь, если вам не нужны данные, а только счет.

Пример: количество новых сообщений, общее количество элементов, когда будет отображаться только страница, и т. Д.

длина

Загружает необходимые данные, то есть запрос по мере необходимости, а затем просто считает их. Путь, если вы используете данные.

Пример: сводка полностью загруженной таблицы, заголовки отображаемых данных и т. Д.

размер

Он проверяет, были ли данные загружены (то есть уже в рельсах), если это так, а затем просто подсчитывает их, в противном случае он вызывает count. (плюс подводные камни, уже упомянутые в других записях).

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

В чем проблема?

Возможно, вы дважды нажмете на БД, если не сделаете это в правильном порядке (например, если вы отобразите количество элементов в таблице поверх визуализированной таблицы, в БД будет фактически отправлено 2 вызова).

estani
источник
3

Следующие стратегии все делают вызов в базу данных для выполнения COUNT(*)запроса.

Model.count

Model.all.size

records = Model.all
records.count

Следующее не так эффективно, так как оно загружает все записи из базы данных в Ruby, который затем подсчитывает размер коллекции.

records = Model.all
records.size

Если у ваших моделей есть ассоциации, и вы хотите найти количество принадлежащих объектов (например @customer.orders.size), вы можете избежать запросов к базе данных (чтение с диска). Используйте счетчик кеша, и Rails будет обновлять значение кеша и возвращать это значение в ответ на sizeметод.

Деннис
источник
2
И то, Model.all.sizeи другое Model.all.countгенерируют countзапрос в Rails 4 и выше. Реальное преимущество sizeзаключается в том, что он не генерирует запрос подсчета, если связь уже загружена. В Rails 3 и ниже я считаю, что Model.allэто не отношение, поэтому все записи уже загружены. Этот ответ может быть устаревшим, и я предлагаю удалить его.
Дэймон Ав
1

Я рекомендовал использовать функцию размера.

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

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

Рассмотрим эти две модели. У клиента есть много действий клиента.

Если вы используете: counter_cache для ассоциации has_many, size будет напрямую использовать кэшированный счетчик и вообще не будет делать дополнительный запрос.

Рассмотрим один пример: в моей базе данных один клиент имеет 20 000 действий клиентов, и я пытаюсь подсчитать количество записей действий клиентов этого клиента с помощью каждого из методов подсчета, длины и размера. здесь ниже сравнительный отчет всех этих методов.

            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)

поэтому я обнаружил, что использование: counter_cache Size - лучший вариант для расчета количества записей.

мантан андхария
источник