この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
巨大なテーブル同士をjoinしてから検索すると遅くなるのでjoinせずに検索する方法を記述します。
例として下記のようなテーブル関連とします。
Post ー* TagPost ー* Tag
|
*
Comment
# 投稿
class Post < ActiveRecord::Base
has_many :comments
has_many :post_tags
has_many :tags, though: :post_tags
end
# 中間テーブル
class PostTag< ActiveRecord::Base
belongs_to :post
belongs_to :tag
end
# タグ
class Tag < ActiveRecord::Base
has_many :post_tags
has_many :posts, though: :post_tags
end
# コメント
class Comment < ActiveRecord::Base
belongs_to :post
end
上記の状態でコメントとタグのタイトルに「あ」という文字が入っている投稿を取得
したい場合はテーブルをjoinして検索すると思います。
Post.joins(:comments, :tags).where(['comments.title LIKE ? AND tags.title LIKE ?', '%あ%', '%あ%' ])
ですががレコード数がそれぞれ数十万件以上
だった場合とてもjoinしていられません。かなり遅くなるかと思います。そこで
それぞれのテーブルでpost_idを取得してそれをもとにPostを取得する
# コメントのpost_id取得
comment_post_ids = Comment.where(['title LIKE ?', '%あ%']).pluck(:post_id)
# タグのid取得
tag_ids = Tag.where(['title LIKE ?', '%あ%']).pluck(:id)
# タグのpost_id取得
tag_post_ids = PostTag.where(tag_id: tag_ids).pluck(:post_ids)
# 取得したpost_idが一致しているものを取得
post_ids = comment_post_ids & tag_post_ids
# 投稿を検索!
posts = Post.where(id: post_ids)
ポイント
- pluckを使う
- 取得したidたちで「&アンド」(どちらかに入っていればいい場合「|オア」)を取る
SQLを複数回実行しますが巨大なテーブル同士をjoinするよりは早くなることがあります。