1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

has_manyで定義されたアソシエーションを順序を指定して効率的にとってきたい

Last updated at Posted at 2018-07-04

問題提起

通常、Railsを使って、N+1問題を解決しつつ、子要素以降にorderを指定しようとすると

Parent.eager_load(:children).order(:name, "children.name")

のように書く形になることが多いと思う。

今回はこの問題に関して本当にこれが正しいのか?を考えてみたい

例えば、下記のようなコードで利用することを考える

parents = Parent.eager_load(:children).order(:name, "children.name")
parents.each do | parent |
  # ここでparentのオブジェクトに対する処理を行う
  p parent
  parents.children.each do | child |
    # ここでchildのオブジェクトに対する処理を行う
    p child
  end
end

上記のような利用シーンでは、ParentにChildのデータがJOINされている意味はなく、
parent.childrenで、希望通りの並び順でchildが取得できていればいいだけ。

変更案1

  • children取得後に並べ替える
    • orderから"children.name"を削除
    • joinする必要がなくなるので、eager_loadからpreloadに変更
parents = Parent.preload(:children).order(:name)
parents.each do | parent |
  # ここでparentのオブジェクトに対する処理を行う
  p parent
  parents.children.sort{ |a, b| a.name <=> b.name }.each do | child |
    # ここでchildのオブジェクトに対する処理を行う
    p child
  end
end

ここで、気をつけてほしいのは、parent.children.order(:name)としてしまうと、せっかくpreloadしたのものが使われず、もう1度データ取得されてしまうこと。
※sortメソッドでやるのも無理やり感ありますが。

変更案2

  • 最初からsortしたアソシエーションを用意してそれを利用する
    • joinは不要になるので、eager_loadをpreloadにする

Parentクラスに、下記のようなアソシエーションを追加する(既存のものを変えてもいい場合は変更したほうが簡単です)

has_many :sorted_children, class_name: "Child", ->{ order(:name) }

呼び出し部分を変える

parents = Parent.preload(:sorted_children).order(:name)
parents.each do | parent |
  # ここでparentのオブジェクトに対する処理を行う
  p parent
  parents.sorted_children.each do | child |
    # ここでchildのオブジェクトに対する処理を行う
    p child
  end
end

JOINの何が悪いのか

データ量が増えれば増えるほど、JOINのコスト、sortのコストは増加していきます。
パフォーマンスを考えた場合、不要なJOINをしないことが一番のパフォーマンスチューニングであると考えているので、なるべくJOINを使わない方法を探しました。

まとめ

個人的には変更案2が無難かなとは考えています。
他にもいい方法があるようであれば、教えてもらいたいです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?