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.