ActiveRecord: rozmiar vs liczba

201

W Railsach możesz znaleźć liczbę rekordów używając zarówno Model.sizei Model.count. Jeśli masz do czynienia z bardziej złożonymi zapytaniami, czy jest jakaś korzyść z używania jednej metody nad drugą? Czym się różnią?

Na przykład mam użytkowników ze zdjęciami. Jeśli chcę wyświetlić tabelę użytkowników i liczbę ich zdjęć, czy uruchomienie wielu wystąpień user.photos.sizebędzie szybsze lub wolniejsze niż user.photos.count?

Dzięki!

Andrzej
źródło

Odpowiedzi:

344

Należy przeczytać , że jest to nadal aktualne.

Dostosujesz funkcję, której używasz, w zależności od potrzeb.

Gruntownie:

  • powiedzmy, że jeśli załadujesz już wszystkie wpisy, User.allpowinieneś użyć, lengthaby uniknąć kolejnego zapytania db

  • jeśli nic nie zostało załadowane, użyj, countaby wykonać zapytanie licznika na db

  • jeśli nie chcesz zawracać sobie głowy tymi rozważaniami, użyj opcji, sizektóra się dostosuje

apneadiving
źródło
35
Jeśli sizedostosowuje się do sytuacji, w każdym razie, to co potrzeba jest tam lengthi countw ogóle?
sscirrus
27
@sscirus - Aby sizemożna było do nich zadzwonić po nawiązaniu połączenia size(po określeniu, do którego należy zadzwonić).
Batkins,
35
Należy jednak zachować ostrożność przy domyślnym ustawianiu rozmiaru. Na przykład, jeśli tworzysz nowy rekord bez przechodzenia przez relacji, czyli Comment.create(post_id: post.id)Twoja post.comments.sizewola nie być aktualne, a post.comments.countwola. Więc bądź ostrożny.
mrbrdo
14
Ponadto, jeśli zbudujesz kilka obiektów za pomocą relacji:, company.devices.build(:name => "device1"); company.devices.build(:name => "device2")wtedy company.devices.sizei .lengthbędzie zawierać liczbę obiektów zbudowanych, ale nie zapisanych, .countzgłosi tylko liczbę z bazy danych.
Shawn J. Goff,
6
@ sscirrus, rozmiar jest niebezpiecznym poleceniem, ponieważ jest zautomatyzowane, czasami trzeba ponownie zapytać o db.
Alex C
79

Jak podają inne odpowiedzi:

  • countwykona COUNTzapytanie SQL
  • length obliczy długość wynikowej tablicy
  • size spróbuje wybrać najbardziej odpowiedni z dwóch, aby uniknąć nadmiernych zapytań

Ale jest jeszcze jedna rzecz. Zauważyliśmy przypadek, w którym sizedziała inaczej count/ lengthzupełnie, i pomyślałem, że go udostępnić, ponieważ jest to rzadki wystarczy pominąć.

  • Jeśli użyjesz atrybutu „as” :counter_cachew has_manypowiązaniu, sizeużyje bezpośrednio pamięci podręcznej i w ogóle nie wykona dodatkowego zapytania.

    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

To zachowanie jest udokumentowane w Przewodnikach po Railsach , ale albo go przegapiłem za pierwszym razem, albo o nim zapomniałem.

Limonka
źródło
W rzeczywistości przed wersją 5.0.0.beta1 takie zachowanie zostałoby uruchomione, nawet jeśli istnieje _countkolumna (bez counter_cache: truedyrektywy w sprawie powiązania). Zostało to naprawione w github.com/rails/rails/commit/e0cb21f5f7
cbliard
8

Czasami size„wybiera niewłaściwy” i zwraca skrót (co countby zrobiło)

W takim przypadku użyj, lengthaby uzyskać liczbę całkowitą zamiast skrótu .

Jvalanen
źródło
Użyłem „.size” w kolekcji z instancji has_many i chociaż w kolekcji był jeden rekord, rozmiar zwracał „0”. Użycie .count zwróciło poprawną wartość „1”.
admazzola
4

tl; dr

  • Jeśli wiesz, że nie będziesz potrzebować danych count.
  • Jeśli wiesz, że wykorzystasz lub wykorzystałeś wykorzystanie danych length.
  • Jeśli nie wiesz, co robisz, użyj size...

liczyć

Rozwiązuje problem z wysłaniem Select count(*)...zapytania do bazy danych. Droga, jeśli nie potrzebujesz danych, ale tylko liczbę.

Przykład: liczba nowych wiadomości, łączna liczba elementów, gdy wyświetlana będzie tylko strona itp.

długość

Ładuje wymagane dane, tj. Zapytanie zgodnie z wymaganiami, a następnie po prostu je zlicza. Droga, jeśli korzystasz z danych.

Przykład: Podsumowanie w pełni załadowanej tabeli, tytuły wyświetlanych danych itp.

rozmiar

Sprawdza, czy dane zostały załadowane (tj. Już w szynach), jeśli tak, to po prostu je policz, w przeciwnym razie wywoła liczyć. (plus pułapki, wspomniane już w innych wpisach).

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

Jaki jest problem?

Że możesz uderzać w DB dwukrotnie, jeśli nie zrobisz tego we właściwej kolejności (np. Jeśli renderujesz liczbę elementów w tabeli nad renderowaną tabelą, do DB zostaną wysłane 2 wywołania).

estani
źródło
3

Wszystkie poniższe strategie wywołują bazę danych w celu wykonania COUNT(*)zapytania.

Model.count

Model.all.size

records = Model.all
records.count

Poniższe nie jest tak wydajne, jak spowoduje załadowanie wszystkich rekordów z bazy danych do Ruby, co następnie zlicza rozmiar kolekcji.

records = Model.all
records.size

Jeśli twoje modele mają powiązania i chcesz znaleźć liczbę przynależnych obiektów (np. @customer.orders.size), Możesz uniknąć zapytań do bazy danych (odczytów dysku). Użyj bufora licznika, a Railsy utrzymają wartość bufora na bieżąco i zwrócą tę wartość w odpowiedzi na sizemetodę.

Dennis
źródło
2
Zarówno Model.all.sizei Model.all.countwygenerować countzapytania w szynach 4 i powyżej. Prawdziwą zaletą sizejest to, że nie generuje zapytania zliczającego, jeśli powiązanie jest już załadowane. W Railsach 3 i niższych uważam, że Model.allnie ma związku, dlatego wszystkie rekordy są już załadowane. Ta odpowiedź może być nieaktualna i sugeruję jej usunięcie.
Damon Aw
1

Polecam użycie funkcji rozmiaru.

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

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

Rozważ te dwa modele. Klient ma wiele działań klienta.

Jeśli użyjesz: counter_cache w powiązaniu has_many, rozmiar użyje bezpośrednio pamięci podręcznej i w ogóle nie wykona dodatkowego zapytania.

Rozważmy jeden przykład: w mojej bazie danych jeden klient ma 20 000 działań klienta i staram się policzyć liczbę zapisów działań klienta tego klienta za pomocą każdej metody liczenia, długości i rozmiaru. poniżej raportu porównawczego wszystkich tych metod.

            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)

więc odkryłem, że użycie: counter_cache Size jest najlepszą opcją do obliczenia liczby rekordów.

Manthan andharia
źródło