その他
    ホーム技術発信DoRuby【Rails】巨大なテーブル同士をjoinせずに検索【速度改善】

    【Rails】巨大なテーブル同士をjoinせずに検索【速度改善】

    この記事はアピリッツの技術ブログ「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するよりは早くなることがあります。