web-dev-qa-db-ja.com

ActiveRecord:サイズとカウント

Railsでは、Model.sizeModel.countの両方を使用してレコードの数を見つけることができます。より複雑なクエリを処理している場合、あるメソッドを他のメソッドよりも使用することに利点はありますか?それらはどう違いますか?

たとえば、写真のあるユーザーがいます。ユーザーの表とユーザーが持っている写真の数を表示したい場合、user.photos.sizeの多くのインスタンスを実行すると、user.photos.countよりも速くなりますか、遅くなりますか?

ありがとう!

189
Andrew

that と読む必要がありますが、それはまだ有効です。

必要に応じて、使用する機能を調整します。

基本的に:

  • User.allなどのすべてのエントリをすでにロードしている場合、lengthを使用して別のdbクエリを回避する必要があります

  • 何もロードしていない場合は、countを使用してデータベースでカウントクエリを作成します。

  • これらの考慮事項に煩わされたくない場合は、sizeを使用してください。

326
apneadiving

他の答えが示すように:

  • countはSQL COUNTクエリを実行します
  • lengthは、結果の配列の長さを計算します
  • sizeは、過剰なクエリを回避するために、2つのうち最も適切なものを選択しようとします

しかし、もう1つあります。 sizecount/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
    

この動作は the Rails Guides に記載されていますが、初めて見逃したか、忘れてしまいました。

79
lime

時々size "間違ったものを選んで" ハッシュを返します(これはcountがすることです)

その場合、lengthを使用してhashではなくintegerを取得します。

7
jvalanen

以下の戦略はすべて、COUNT(*)クエリを実行するためにデータベースを呼び出します。

Model.count

Model.all.size

records = Model.all
records.count

以下は、データベースからすべてのレコードをRubyにロードしてからコレクションのサイズをカウントするので、効率的ではありません。

records = Model.all
records.size

モデルに関連付けがあり、所属するオブジェクトの数(@customer.orders.sizeなど)を検索する場合、データベースクエリ(ディスク読み取り)を回避できます。 counter cache を使用すると、Railsはキャッシュ値を最新の状態に保ち、sizeメソッドへの応答としてその値を返します。

3
Dennis

サイズ関数を使用することをお勧めします。

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

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

これらの2つのモデルを検討してください。顧客には多くの顧客アクティビティがあります。

Has_manyアソシエーションで:counter_cacheを使用する場合、sizeはキャッシュされたカウントを直接使用し、余分なクエリは一切行いません。

1つの例を考えてみましょう。私のデータベースでは、1人の顧客が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 Sizeを使用するのが最適なオプションであることがわかりました。

1

tl; dr

  • データが必要ないことがわかっている場合は、countを使用してください。
  • データを使用する、または使用したことがわかっている場合は、lengthを使用してください。
  • 何をしているのかわからない場合は、size...を使用してください.

カウント

Select count(*)...クエリをDBに送信することを解決します。データは必要なく、カウントだけが必要な場合の対処法。

例:新しいメッセージの数、ページのみが表示される場合の合計要素など。

長さ

必要なデータ、つまり必要に応じてクエリを読み込み、それをカウントします。データを使用している場合の方法。

例:完全にロードされたテーブルの概要、表示されたデータのタイトルなど。

サイズ

データがロードされたかどうか(つまり、すでにRailsにあるかどうか)をチェックし、ロードされている場合はカウントします。そうでない場合はcountを呼び出します。 (さらに、他のエントリで既に述べた落とし穴)。

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

どうしたの?

正しい順序で実行しないとDBに2回ヒットする可能性があります(たとえば、レンダリングされたテーブルの上にあるテーブルの要素の数をレンダリングすると、DBに事実上2つの呼び出しが送信されます)。

0
estani