railsで毎回データベースに問い合わせる判定処理を書いてしまったとき、データベースにpluckで一覧データを作成しよう
1つのSQLの発行をなくすと、5ms早くなります(自分の運営サイト)
それが一覧ページだと20回以上発行されていて、無駄に重くなっていたのですが、解決しました。
毎回1SQLが発行されると処理が重くなるのは、前回の記事を見て頂いたら、わかるかと思います。
まずはモデル関係の整理です。
User
class User < ActiveRecord::Base has_many :grumbles, dependent: :destroy has_many :cheers end
Post
class Post < ActiveRecord::Base acts_as_taggable belongs_to :user has_many :cheers end
Cheer
class Cheer < ActiveRecord::Base belongs_to :user belongs_to :grumble, counter_cache: true end
では、どういう式かと言いますと、helperにこう書いてありました。
内容は、userが持っているpostに対して、応援をしているかどうかを判定している式です。
def cheer_attrs(user, post) if !user 'signin_required' elsif post.user == user 'is-owner' elsif Cheer.where(user_id: user.id, post_id: post.id).exists? ←問題の箇所 'on' else 'off' end end
これの何が原因かと言いますと、判定処理に毎回SQLが発行されてしまいます。
これを一覧ページ(index)で出していたので、毎回SQLが発行されてしまい、無駄に重くなってしまいました。
最初にuserが持っているpostに対して、cheerをしたデータを作っておけば、そこにあるかの判定をするだけでよくなります。
そこで、userが持っているpostに対して、cheerをした一覧を作成します。
controller
private def user_set_cheers @user_cheers = current_user.cheers.pluck(:post_id) ←ここのpluckが重要 end
pluckは任意のカラムに対して、配列を作成してくれます。
つまり、最初にモデルから配列を作成しておけば、あとは配列があるかないかだけの判定になります。
これであとはページを開いたときに、一覧データを作成するようにすればいいですね。
helper
def cheer_attrs(user, post) if !user 'signin_required' elsif post.user == user 'is-owner' elsif @user_cheers.include?(post.id)← trueかfalseだけになる 'on' else 'off' end end
これで配列に含まれているかだけのtrue or falseのデータ処理に変わります。
最初に全体のsqlを発行するだけになります。
これを書くだけで約100ms(0.1秒)早くなりました。
SQLって重要ですね。