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!

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


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.


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 .


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).


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ę.


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.