railsの親モデルから子モデルへのcountをするのは、counter_cacheを使ってみよう

サイトのパフォーマンスをチェックしていたら、おぞましいSQLの発行が行われており、サイトが重くなっていました。

f:id:frozen_faithjp:20160319225040p:plain

これは気持ち悪いですね。

こんな大量にSQLが発行されていたら、表示速度が無駄に遅くなります。

では、話を今回のモデルの概要です。

Post model(親)に、Cheer model(子)が属しています。

Post model

class Grumble < ActiveRecord::Base
  has_many :cheers
end

Cheer model

class Cheer < ActiveRecord::Base
  belongs_to :grumble, counter_cache: true
end

これに対して、view側での表示の仕方です。

post.cheers.count

発行されていたSQLの中身です。

SELECT COUNT(*) FROM "cheers" WHERE "cheers"."post_id" = ?  

SELECT COUNT(*)となっているので、毎回全力でCheerモデルから、post_idを全部のデータから探しています。

これは時間がかかるわけだ。

railsでは、こういう場合の時のために、counter_cacheという機能があります。

counter_cacheを使用すれば、上のSQLの発行がなくなります。

使い方です。

今回の例では、Post model(親)にcheers_countというcolumnを作ります。

rails g migration AddCheersCountToPost cheers_count:integer

migationにdefault: 0を付け加えます。

class AddCheersCountToPost < ActiveRecord::Migration
  def change
    add_column :posts, :cheers_count, :integer, default: 0
  end
end

この後に、Cheer model(子)にcounter_cacheというオプションをつけます。

class Cheer < ActiveRecord::Base
  belongs_to :grumble, counter_cache: true
end

最後にview側の設定です。

post.cheers.size

ここをsizeにしないといけません。

これでSQLの発行がされなくなります。

サイトの表示速度は重要なパフォーマンスなんで、気をつけていきたいですね。