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


201

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

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

Спасибо!

Ответы:


344

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

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

В принципе:

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

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

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


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чтобы получить целое число вместо хеша .


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

4

ТЛ; др

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

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

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

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

длина

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

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

размер

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

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

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

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


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 - лучший вариант для расчета количества записей.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.