围绕Rails #joins与#includes的混淆

时间:2015-09-03 18:46:47

标签: ruby-on-rails activerecord

这实际上只是为了解释为什么连接子句对我正在做的事情是必要的。两者都返回我想要的所有内容,但查询不同,一个不太理想:

TL; DR:当我将连接与包含组合时,Rails正在做什么,我不会认为我真的需要连接。感谢愿意真正读过这个怪物的人。

注意:object是一个ToDoListsUser对象,这一切都在导出器中发生,因为它的价值。而ProgressItem是User和ToDoItem之间的连接表。

使用joins子句(查询看起来很好并且符合预期):

object.to_do_list.progress_items.where(user_id: object.user_id)
                                .joins(:to_do_item)
                                .includes(:to_do_item)

服务器输出:

ToDoListsUser Load (0.5ms)  SELECT "to_do_lists_users".* FROM "to_do_lists_users" WHERE "to_do_lists_users"."user_id" = $1  ORDER BY "to_do_lists_users"."id" DESC OFFSET 0  [["user_id", 543]]
ToDoList Load (0.3ms)  SELECT  "to_do_lists".* FROM "to_do_lists" WHERE "to_do_lists"."id" = $1 LIMIT 1  [["id", 144]]

SQL (1.6ms) SELECT "progress_items"."id" AS t0_r0, "progress_items"."completed" AS t0_r1, "progress_items"."user_id" AS t0_r2, "progress_items"."to_do_item_id" AS t0_r3, "progress_items"."created_at" AS t0_r4, "progress_items"."updated_at" AS t0_r5, "progress_items"."hidden" AS t0_r6, "to_do_items_progress_items"."id" AS t1_r0, "to_do_items_progress_items"."to_do_list_id" AS t1_r1, "to_do_items_progress_items"."description" AS t1_r2, "to_do_items_progress_items"."date_due" AS t1_r3, "to_do_items_progress_items"."created_at" AS t1_r4, "to_do_items_progress_items"."updated_at" AS t1_r5, "to_do_items_progress_items"."name" AS t1_r6, "to_do_items_progress_items"."element_id" AS t1_r7, "to_do_items_progress_items"."linkable_id" AS t1_r8, "to_do_items_progress_items"."linkable_type" AS t1_r9, "to_do_items_progress_items"."action" AS t1_r10 FROM "progress_items" INNER JOIN "to_do_items" "to_do_items_progress_items" ON "to_do_items_progress_items"."id" = "progress_items"."to_do_item_id" INNER JOIN "to_do_items" ON "progress_items"."to_do_item_id" = "to_do_items"."id" WHERE "to_do_items"."to_do_list_id" = $1 AND "progress_items"."user_id" = $2 ORDER BY to_do_items.created_at [["to_do_list_id", 144], ["user_id", 543]]

ToDoList Load (0.4ms)  SELECT  "to_do_lists".* FROM "to_do_lists" WHERE "to_do_lists"."id" = $1 LIMIT 1  [["id", 133]]

SQL (3.3ms) SELECT "progress_items"."id" AS t0_r0, "progress_items"."completed" AS t0_r1, "progress_items"."user_id" AS t0_r2, "progress_items"."to_do_item_id" AS t0_r3, "progress_items"."created_at" AS t0_r4, "progress_items"."updated_at" AS t0_r5, "progress_items"."hidden" AS t0_r6, "to_do_items_progress_items"."id" AS t1_r0, "to_do_items_progress_items"."to_do_list_id" AS t1_r1, "to_do_items_progress_items"."description" AS t1_r2, "to_do_items_progress_items"."date_due" AS t1_r3, "to_do_items_progress_items"."created_at" AS t1_r4, "to_do_items_progress_items"."updated_at" AS t1_r5, "to_do_items_progress_items"."name" AS t1_r6, "to_do_items_progress_items"."element_id" AS t1_r7, "to_do_items_progress_items"."linkable_id" AS t1_r8, "to_do_items_progress_items"."linkable_type" AS t1_r9, "to_do_items_progress_items"."action" AS t1_r10 FROM "progress_items" INNER JOIN "to_do_items" "to_do_items_progress_items" ON "to_do_items_progress_items"."id" = "progress_items"."to_do_item_id" INNER JOIN "to_do_items" ON "progress_items"."to_do_item_id" = "to_do_items"."id" WHERE "to_do_items"."to_do_list_id" = $1 AND "progress_items"."user_id" = $2 ORDER BY to_do_items.created_at [["to_do_list_id", 133], ["user_id", 543]]

VS

没有joins子句(查询不是最佳的):

object.to_do_list.progress_items.where(user_id: object.user_id)
                                .includes(:to_do_item)

服务器输出:

ToDoListsUser Load (0.3ms)  SELECT "to_do_lists_users".* FROM "to_do_lists_users" WHERE "to_do_lists_users"."user_id" = $1  ORDER BY "to_do_lists_users"."id" DESC OFFSET 0  [["user_id", 543]]
ToDoList Load (0.4ms)  SELECT  "to_do_lists".* FROM "to_do_lists" WHERE "to_do_lists"."id" = $1 LIMIT 1  [["id", 144]]
ProgressItem Load (2.0ms)  SELECT "progress_items".* FROM "progress_items" INNER JOIN "to_do_items" ON "progress_items"."to_do_item_id" = "to_do_items"."id" WHERE "to_do_items"."to_do_list_id" = $1 AND "progress_items"."user_id" = $2  ORDER BY to_do_items.created_at  [["to_do_list_id", 144], ["user_id", 543]]
ToDoList Load (0.5ms)  SELECT  "to_do_lists".* FROM "to_do_lists" WHERE "to_do_lists"."id" = $1 LIMIT 1  [["id", 133]]
ProgressItem Load (0.5ms)  SELECT "progress_items".* FROM "progress_items" INNER JOIN "to_do_items" ON "progress_items"."to_do_item_id" = "to_do_items"."id" WHERE "to_do_items"."to_do_list_id" = $1 AND "progress_items"."user_id" = $2  ORDER BY to_do_items.created_at  [["to_do_list_id", 133], ["user_id", 543]]
ToDoItem Load (0.4ms)  SELECT "to_do_items".* FROM "to_do_items" WHERE "to_do_items"."id" IN (193, 194, 195, 196)

1 个答案:

答案 0 :(得分:1)

来自ActiveRecord documentation

  

<强>条件

     

如果要为包含的模型添加条件,则必须明确引用它们。例如:

User.includes(:posts).where('posts.name = ?', 'example')
     

会抛出错误,但这会有效:

User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
     

请注意,include包含关联名称,而引用需要实际的表名。

这种情况发生了,因为实际上include的Rails没有连接,它会抓取两个请求的记录。

例如:

class List < ActiveRecord::Base
  has_many :tasks
end

List.includes(:tasks)
# List Load (1.9ms)  SELECT "lists".* FROM "lists"
# Task Load (0.8ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."list_id" IN (1, 2, 3)

但随着references,它又回归加入:

List.includes(:tasks).references(:tasks)
# SQL (0.6ms)  SELECT "lists"."id" AS t0_r0, "lists"."name" AS t0_r1,
#              "tasks"."id" AS t1_r0, "tasks"."name" AS t1_r1 FROM
#              "lists" LEFT OUTER JOIN "tasks" ON "tasks"."list_id" = "lists"."id"