【Rails】sizeとcountとlengthについて

古いソースを眺めていて気づいたことなんだけど、いや、うん、というかちょっとこれは・・と思う書き方があったので。

class Yukkuri < ActiveRecord::Base
end

と、いつものモデルがあって、

このモデルのyukkurisテーブルの全レコード数を確認しようとして、だとは思うんだけど、

p Yukkuri.all.size

なるコードが書いてあった・・・



いやぁこれはないなと。



これって要は、掃き出されるSQLを見るとよくわかるけど、
こう書いた場合のsizeメソッドは、配列のサイズを取得するだけなので、select count(*) from yukkuris 〜 といったSQLは発行されず、(きっと?)期待しているメソッドチェーンにはならない。

@yukkuri = Yukkuri.all
p @yukkuri.size

と書くのとまったく一緒で、yukkuris テーブルの全レコードを取得して、Yukkuriオブジェクトの配列を作り、その配列のsizeを調べている。というだけなんですよね。


これをやりたいなら、

Yukkuri.count

これだけ。

これで発行されるSQL

select count(*) from yukkuris 〜

となる。


書き方だけ見ると一見同じように見えるけど、レコード数が多くなるほどレスポンスが目に見えて違ってくるんですよね。





てなわけで、本題。

いろんなサイトでもsizeとcountとlengthの違いについては記載されていますが、改めてその違いについて。



まず、3者ともrubyのArrayクラス、Hashクラスのメソッドとして使えます。(他にも色んなとこで出てきます・・)

なので、

[1, 2, 3, 4, 5].size

とか

{:hoge => 1, :fuga => 2}.length

とか

[1, 2].count

などのように使います。
違いはまぁありませんね。


次に、たとえばrubyのFixnumクラスやBignumクラスはsizeメソッドのみで、lengthやcountは使えません。

Fixnumの場合だと、これは31ビットまたは63ビットの固定長整数を扱うクラスなので、
sizeメソッドが返す値は、何のマシン上で実行してるかにより結果は変わりますし(変わるよね?)、正直自分はこの用途で利用したことはありませんw

たとえば、32bitマシンなら

123.size
=> 4

みたいな感じですね。


Stringクラスでは、size、lengthは使えますが、countは単体では使えません。
文字数を数えるメソッドですね。

"123aaabbb".size

12345.to_s.length

と使え、

"hoge".count

はエラーになります。
ただ、countは引数を受けて、そのオブジェクト内に含まれる引数で指定した文字列の文字数を返すので

"yukkuri".count("u")
=> 2

となり、有効っちゃ有効です。


あと、上の例のようにモデルのテーブルにどのくらいのレコードがあるかを調べる場合、countが使えますが、sizeやlengthは使えません。
Active Recordの集計関数として、countは定義されていますが、sizeやlengthは定義されていないためです。

Yukkuri.count

は有効ですが、

Yukkuri.size

はエラーになります。

同じく集計関数として定義されているものは、
おなじみの、average や sum、maximum などです。

Yukkuri.maximum(:age)

みたいな感じ。


また、モデル間でhas_manyのアソシエーションを組んでいる場合はもうちょっと複雑で、
特にsizeメソッドはカウンタキャッシュを使っている場合(belongs_to に :counter_cache=> true と仕込むアレ。)と使っていない場合で挙動が違います。
さらに、sizeもlengthもオブジェクトがロード済が否かでも挙動が違います・・・
カウンタキャッシュを使っていない場合、
sizeは、ロード時には配列の要素数を返し、未ロード時にはcount文を発行しますし、
lengthは、ロード時には配列の要素数を返しますが、未ロード時にはselect * 〜を発行したのち配列の要素数を返します。
そんでもって、
countはどんなときであれ毎回count文を発行します。

動きは一見同じように見えるけど、どの時にどのメソッドを選択するかで動きが変わり、それによって速度がだいぶかわるってわけです。
countが毎回良いわけでもなく、lengthが毎回良いわけでもない。という(´・ω・`;)



細かいこといえばまだまだ足りないですが、それはまた機会があったら。




ま。



なんというか。



Railsは似たような意味だけど厳密には違うっていうメソッド山ほどありますよね・・



っていう。。