ActiveRecord: розмір проти підрахунку


201

У Rails можна знайти кількість записів, використовуючи і Model.sizeі, і Model.count. Якщо ви маєте справу зі складнішими запитами, чи є якась перевага використання одного методу над іншим? Чим вони відрізняються?

Наприклад, у мене є користувачі з фотографіями. Якщо я хочу показати таблицю користувачів і скільки фотографій у них, чи буде багато примірників user.photos.sizeшвидше чи повільніше, ніж user.photos.count?

Дякую!

Відповіді:


344

Ви повинні прочитати це , воно все ще діє.

Ви адаптуєте функцію, яку ви використовуєте, залежно від ваших потреб.

В основному:

  • якщо ви вже завантажуєте всі записи, скажімо User.all, тоді вам слід скористатися, lengthщоб уникнути іншого запиту на db

  • якщо ви нічого не завантажили, використовуйте, countщоб зробити запит підрахунку на ваш db

  • якщо ви не хочете турбуватися з цими міркуваннями, використовуйте, 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, розмір - це небезпечна команда, оскільки вона автоматизована, іноді ви хочете запитати DB знову.
— Алекс C

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

Така поведінка задокументована в Посібниках по рейках , але я або пропустив її вперше, або забув про неї.


Насправді, перед рейками 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".
— адмаццола

4

тл; д-р

  • Якщо ви знаєте, вам не потрібно буде використовувати дані count.
  • Якщо ви знаєте, ви будете використовувати або використовували дані length.
  • Якщо ви не знаєте, що робите, скористайтеся size...

рахувати

Вирішує надіслати Select 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це не стосується, тому всі записи вже завантажені. Ця відповідь може бути застарілою, і я пропоную її видалити.
— Damon Aw

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, розмір буде використовувати кешований підрахунок безпосередньо, а не робити зайвий запит взагалі.

Розглянемо один приклад: у моїй базі даних один клієнт має 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 Розмір є найкращим варіантом для обчислення кількості записів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.