2016-11-17 24 views
2

試圖找出一種有效的方法來選擇跨多個表具有屬性的記錄。這裏的基本設置:如何在Rails4中的多個表中構建高效的選擇命令?

structure

  • 植物(字段:ID,名_ID,LOCATION_ID,彩色)(1000條記錄)
  • 名稱(字段:ID,COM​​MON_NAME)(60筆)
  • 地點(字段:ID,Bed_name)(125條記錄)

model

  • 植物 - belongs_to的名稱,belongs_to的位置
  • 名稱 - 的has_many植物
  • 位置 - 的has_many植物

我的目標是要輸出每朵玫瑰在院子一側的列表,並顯示顏色,但我卡在選擇命令。如果我得到所有植物(p = Plant.all),我知道我可以很容易地創建我的輸出,如<%= "#{p.name.common_name} in bed #{p.location.bed_name} has a color of #{p.color}" %> 如果我做了兩個連接,我正在尋找更多的記錄,我需要和更長的搜索時間。舉個例子 - 我在16張不同的牀上有67朵玫瑰花,但是我在院子裏只有3朵玫瑰花。

我的直覺告訴我,我應該可以這樣做: 選擇名字爲Rose的所有植物,然後從這個選擇中選擇在院子裏的所有玫瑰。

有人能幫我指出正確的方向嗎?

回答

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" }) 

這將加載相關記錄nameslocationsincludes便於消除(或至少減少)N + 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

謝謝@Dan ,這樣好多了。 –