ActiveRecord: kích thước so với số lượng


201

Trong Rails, bạn có thể tìm thấy số lượng bản ghi bằng cả hai Model.sizeModel.count. Nếu bạn đang xử lý các truy vấn phức tạp hơn, có cách nào để sử dụng một phương thức này so với phương thức khác không? Họ khác nhau như thế nào?

Ví dụ, tôi có người dùng với hình ảnh. Nếu tôi muốn hiển thị bảng người dùng và họ có bao nhiêu ảnh, sẽ chạy nhiều trường hợp user.photos.sizenhanh hơn hoặc chậm hơn user.photos.count?

Cảm ơn!

Câu trả lời:


344

Bạn nên đọc rằng , nó vẫn còn hợp lệ.

Bạn sẽ điều chỉnh chức năng bạn sử dụng tùy thuộc vào nhu cầu của bạn.

Về cơ bản:

  • nếu bạn đã tải tất cả các mục, giả sử User.all, thì bạn nên sử dụng lengthđể tránh truy vấn db khác

  • nếu bạn chưa tải gì, hãy sử dụng countđể tạo truy vấn đếm trên db của bạn

  • Nếu bạn không muốn bận tâm với những cân nhắc này, hãy sử dụng sizenó sẽ thích nghi


35
Nếu sizethích nghi với tình huống nào, thì cần gì ở đó lengthcounttất cả?
sscirrus

27
@sscirus - Vì vậy, sizecó thể thực hiện cuộc gọi cho họ khi bạn thực hiện cuộc gọi đến size(sau khi xác định cuộc gọi nào sẽ gọi).
Batkins

35
Tuy nhiên, hãy cẩn thận với kích thước mặc định. Ví dụ: nếu bạn tạo một bản ghi mới mà không trải qua mối quan hệ, nghĩa là Comment.create(post_id: post.id), bạn post.comments.sizesẽ không cập nhật, trong khi post.comments.countsẽ. Vì vậy, hãy cẩn thận.
mrbrdo

14
Ngoài ra, nếu bạn xây dựng một số đối tượng thông qua một mối quan hệ : company.devices.build(:name => "device1"); company.devices.build(:name => "device2"), sau đó company.devices.size.lengthsẽ bao gồm số lượng đối tượng bạn đã xây dựng nhưng chưa lưu, .countsẽ chỉ báo cáo số lượng từ cơ sở dữ liệu.
Shawn J. Goff

6
@sscirrus, kích thước là một lệnh nguy hiểm vì nó tự động, đôi khi bạn muốn truy vấn lại db.
Alex C

79

Như các câu trả lời khác nêu:

  • countsẽ thực hiện một COUNTtruy vấn SQL
  • length sẽ tính toán độ dài của mảng kết quả
  • size sẽ cố gắng chọn cách thích hợp nhất trong hai để tránh truy vấn quá mức

Nhưng có một điều nữa. Chúng tôi nhận thấy một trường hợp sizehành động khác với count/ lengthhoàn toàn và tôi nghĩ tôi muốn chia sẻ nó vì nó hiếm khi bị bỏ qua.

  • Nếu bạn sử dụng :counter_cachetrên một has_manyhiệp hội, sizesẽ sử dụng Cached đếm trực tiếp, và không phải thực hiện một truy vấn thêm ở tất cả.

    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
    

Hành vi này được ghi lại trong Hướng dẫn Rails , nhưng tôi đã bỏ lỡ lần đầu tiên hoặc quên nó.


Trong thực tế, trước khi rails 5.0.0.beta1, hành vi này sẽ được kích hoạt ngay cả khi có một _countcột (không có counter_cache: truechỉ thị về liên kết). Điều này đã được sửa trong github.com/rails/rails/commit/e0cb21f5f7
cbliard

8

Đôi khi size"chọn sai" và trả về hàm băm (đó là những gì countsẽ làm)

Trong trường hợp đó, sử dụng lengthđể lấy số nguyên thay vì hàm băm .


Tôi đã sử dụng '.size' trên Bộ sưu tập từ một ví dụ has_many và mặc dù có một bản ghi trong bộ sưu tập, kích thước đã trả về '0'. Sử dụng .count trả về giá trị đúng của '1'.
admazzola

4

tl; dr

  • Nếu bạn biết bạn sẽ không cần sử dụng dữ liệu count.
  • Nếu bạn biết bạn sẽ sử dụng hoặc đã sử dụng dữ liệu sử dụng length.
  • Nếu bạn không biết bạn đang làm gì, hãy sử dụng size...

đếm

Giải quyết gửi một Select count(*)...truy vấn đến DB. Cách để đi nếu bạn không cần dữ liệu, nhưng chỉ cần đếm.

Ví dụ: số lượng tin nhắn mới, tổng số phần tử khi chỉ một trang sẽ được hiển thị, v.v.

chiều dài

Tải dữ liệu cần thiết, tức là truy vấn theo yêu cầu, và sau đó chỉ cần đếm nó. Cách để đi nếu bạn đang sử dụng dữ liệu.

Ví dụ: Tóm tắt về bảng được tải đầy đủ, tiêu đề của dữ liệu được hiển thị, v.v.

kích thước

Nó kiểm tra nếu dữ liệu đã được tải (tức là đã có trong đường ray) nếu vậy, sau đó chỉ cần đếm nó, nếu không nó sẽ đếm. (cộng với những cạm bẫy, đã được đề cập trong các mục khác).

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

Có vấn đề gì vậy?

Rằng bạn có thể nhấn DB hai lần nếu bạn không thực hiện theo đúng thứ tự (ví dụ: nếu bạn kết xuất số phần tử trong một bảng trên đầu bảng được kết xuất, sẽ có hiệu quả 2 cuộc gọi được gửi đến DB).


3

Tất cả các chiến lược sau đây thực hiện một cuộc gọi đến cơ sở dữ liệu để thực hiện một COUNT(*)truy vấn.

Model.count

Model.all.size

records = Model.all
records.count

Việc sau đây không hiệu quả vì nó sẽ tải tất cả các bản ghi từ cơ sở dữ liệu vào Ruby, sau đó sẽ tính kích thước của bộ sưu tập.

records = Model.all
records.size

Nếu mô hình của bạn có các liên kết và bạn muốn tìm số lượng đối tượng thuộc (ví dụ @customer.orders.size), bạn có thể tránh các truy vấn cơ sở dữ liệu (đọc đĩa). Sử dụng bộ đệm bộ đếm và Rails sẽ giữ cho giá trị bộ đệm được cập nhật và trả về giá trị đó để đáp ứng với sizephương thức.


2
Cả hai Model.all.sizeModel.all.counttạo một counttruy vấn trong Rails 4 trở lên. Ưu điểm thực sự sizelà nó không tạo ra truy vấn đếm nếu liên kết đã được tải. Trong Rails 3 trở xuống, tôi tin rằng Model.allkhông phải là một mối quan hệ, do đó tất cả các bản ghi đã được tải. Câu trả lời này có thể đã lỗi thời và tôi khuyên bạn nên xóa nó.
Damon Aw

1

Tôi khuyên bạn nên sử dụng chức năng kích thước.

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

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

Hãy xem xét hai mô hình này. Khách hàng có nhiều hoạt động của khách hàng.

Nếu bạn sử dụng: counter_cache trên liên kết has_many, kích thước sẽ sử dụng trực tiếp số lượng được lưu trong bộ nhớ cache và không thực hiện thêm một truy vấn nào.

Hãy xem xét một ví dụ: trong cơ sở dữ liệu của tôi, một khách hàng có 20.000 hoạt động của khách hàng và tôi cố gắng đếm số lượng hồ sơ hoạt động của khách hàng của khách hàng đó với mỗi phương pháp đếm, độ dài và kích thước. ở đây dưới báo cáo điểm chuẩn của tất cả các phương pháp này.

            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)

vì vậy tôi thấy rằng sử dụng: counter_cache Size là tùy chọn tốt nhất để tính số lượng bản ghi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.