1

Enumerable#selectを使用して作成したこのクエリがあります。その目的は、レコードにhas manyのレコードがないレコードを見つけることです。レコードがある場合は、preview属性をtrueに設定したレコードだけを選択します。以下のコードは、そのユースケースに対して完全に機能します。ただし、このクエリは拡張されません。何千ものレコードに対してテストすると、完了するまでに数百秒かかります。どのようにしてこのクエリを改善できますか?最適化には多くのレコード関連クエリがあります

# User has many enrollments 
# Enrollment belongs to user. 

users_with_no_courses = User.includes(:enrollments).select {|user| user.enrollments.empty? || user.enrollments.where(preview: false).empty?} 

答えて

2

だからまずenrollments.user_idにインデックスがあることを確認してください。

第二に、あなたはすべての入学をロードし、SQLであなたのフィルタリングを行っていないことで、これをスピードアップすることができません:

User.where(<<-EOQ) 
    NOT EXISTS (SELECT 1 
       FROM enrollments e 
       WHERE e.user_id = users.id 
       AND NOT e.preview) 
EOQ 

私は1つにあなたの二つの条件を簡素化していますここちなみに:「何の入学者をか実際の登録なし "は"実際の登録なし "と同じです。

この条件をscopeに入れることができますので、再利用可能です。

第3に、何千ものUserオブジェクトをインスタンス化すると、これはまだ遅くなります。だから、もしそれが意味をなさないならばページ付けするか、またはこれがオフラインのスクリプトならばfind_eachを調べるだろう。または、生のSQLを使用して、すべてのオブジェクト・インスタンスを回避します。道による

ああ:

user.enrollments.where(preview: false) 

whereはActiveRecordのではないことを意味しているためである:あなたがincludes(:enrollments)を言っているにもかかわらず、これはまだあなたのn + 1つの問題を与えて、戻ってデータベースに移動しますすでにロードされている関連付けを使用します。 whereの代わりにselectを使用することで回避できます。しかし、最初にenrollmentsを読み込まない方がさらに優れています。

+0

素晴らしい。詳細な答えをありがとう! – jason328

関連する問題