2016-11-17 4 views
2

複数の表にまたがって属性を持つレコードを選択する効率的な方法を見つけようとしています。ここでは基本的な設定があります:Rails4の複数のテーブルにわたって効率的な選択コマンドを作成するには?

structure

  • 植物(フィールド:ID、NAME_ID、location_idの、色)(1000年の記録)
  • 名(フィールド:ID、COMMON_NAME)(50件のレコード)
  • 位置(フィールド:ID、Bed_name)(125件のレコード)

model

  • 植物 - 名前belongs_toの、場所belongs_toの
  • 名 - has_manyの植物
  • 場所 - has_manyの植物

私の目標は、出力にサイドヤード内のすべてのバラのリストであり、そして色を表示、しかし、私は選択コマンドに固執しています。 2つの結合を行うと、必要なレコードが増え、検索時間が長くなります。すべての植物(p = Plant.all)を取得すると、簡単に出力を作成できることがわかります。一例として、私は16の異なるベッドで67のバラを持っていますが、私はサイド・ヤードに3つのバラしかありません。

私は、次のようなことができるはずです: ローズの名前のすべての植物を選択し、この選択肢からサイドヤードにあるすべてのローズを選択します。

誰かが正しい方向に私を向けるのに役立つことができますか?

答えて

2

次のような単一のクエリにそれをすべて組み合わせることができます。

Plant.joins(:name, :location).where(names: { common_name: "rose" }, locations: { bed_name: "side" }) 

これは、このような単一のSQLクエリで結果:あなたは、複数のテーブルを使用する必要が

SELECT "plants".* FROM "plants" INNER JOIN "names" ON "names"."id" = "plants"."name_id" INNER JOIN "locations" ON "locations"."id" = "plants"."location_id" WHERE "names"."common_name" = 'rose' AND "locations"."bed_name" = 'side' 

注意をwhere句にはという名前がありますが、joins句には、という関連名があります。

これは、テーブルが適切にインデックスされていると仮定すると、巨大なテーブルであってもほとんど瞬時に実行されます。

これは簡単な例ですが、条件付きのかなり複雑な結合を実行できます。詳細はActiveRecord documentationをご覧ください。ダンさんのコメント@パー

編集

、あなたが参加中の関連データをプリフェッチするincludesを使用して、よりこれをスピードアップすることができます。

Plant.includes(:name, :location).where(names: { common_name: "rose" }, locations: { bed_name: "side" }) 

これから関連レコードをロードしますnameslocationsを同時に使用してください。includesは、N + 1個のクエリを排除する(または少なくとも減らす)のに便利です。また、1つのクエリ内のすべてのデータをいつ取り出すことができるかを知るには十分にスマートで、それが意味を成すときは複数のクエリにフォールバックします。それについて考える必要はありません(効率が低下することがありますが、パフォーマンスが低下すると思われる場合はログに注目してください)。この場合

使用includesは、関連データを含む単一のSQLクエリで、その結果、非常に効率的である:私は、ネストされた構文を逃したが、それは私が必要な正確に何だったか

SELECT "plants"."id" AS t0_r0, "plants"."color" AS t0_r1, "plants"."name_id" AS t0_r2, "plants"."location_id" AS t0_r3, "plants"."created_at" AS t0_r4, "plants"."updated_at" AS t0_r5, "names"."id" AS t1_r0, "names"."common_name" AS t1_r1, "names"."created_at" AS t1_r2, "names"."updated_at" AS t1_r3, "locations"."id" AS t2_r0, "locations"."bed_name" AS t2_r1, "locations"."created_at" AS t2_r2, "locations"."updated_at" AS t2_r3 FROM "plants" LEFT OUTER JOIN "names" ON "names"."id" = "plants"."name_id" LEFT OUTER JOIN "locations" ON "locations"."id" = "plants"."location_id" WHERE "names"."common_name" = 'rose' AND "locations"."bed_name" = 'side' 
+0

わかりません!私のクエリは6000msから60msになりました! :) – Ben

+2

パフォーマンスをさらに向上させるために '.includes(:name、:location)'を追加することをお勧めします。ここで少し話題を読んでいます:https://code.tutsplus.com/articles/improving-the-performance-of-your-rails-app-with-eager-loading--cms-25018 – Dan

+0

ありがとう@ダン、それははるかに良い方法。 –

関連する問題