当前位置:  开发笔记 > 后端 > 正文

比较包括与eager_load的.references要求

如何解决《比较包括与eager_load的.references要求》经验,为你挑选了1个好方法。

我知道当你使用includeswhere在连接表上指定一个子句时,你应该使用.references

例:

# will error out or throw deprecation warning in logs
users = User.includes(:orders).where("Orders.cost < ?", 20)

在rails 4或更高版本中,您将收到如下错误:

Mysql2 ::错误:'where子句'中的未知列'Orders.cost':SELECT customers.*FROM customers WHERE(Orders.cost <100)

或者您将收到弃用警告:

弃用警告:您似乎急于加载在字符串SQL片段中引用的表(用户,地址之一).例如:

Post.includes(:comments).where("comments.title ='foo'")目前,Active Record识别字符串中的表,并且知道将comments表连接到查询,而不是在单独的查询中加载注释.但是,在不编写完整的SQL解析器的情况下执行此操作本身就存在缺陷.由于我们不想编写SQL解析器,因此我们将删除此功能.从现在开始,当您从字符串引用表时,必须明确告诉Active Record:

Post.includes(:comments).where("comments.title ='foo'").references(:comments)

如果您不依赖隐式连接引用,则可以通过设置config.active_record.disable_implicit_join_references = true来完全禁用该功能.(

SELECT"users"."id"AS t0_r0,"users"."name"AS t0_r1,"users"."email"AS t0_r2,"users"."created_at"AS t0_r3,"users"."updated_at"AS t0_r4 ,"地址"."id"AS t1_r0,"地址"."user_id"AS t1_r1,"地址"."country"AS t1_r2,"地址"."street"AS t1_r3,"地址"."postal_code"AS t1_r4 ,"地址"."city"AS t1_r5,"地址"."created_at"AS t1_r6,"地址"."updated_at"AS t1_r7 FROM"users"LEFT OUTER JOIN"地址"ON"地址"."user_id"="用户"."id"WHERE(addresses.country ='Poland')

所以我们这样做:

# added .references(:orders)
users = User.includes(:orders).where("Orders.cost < ?", 20).references(:orders)

它执行得很好:

SELECT "users"."id"     AS t0_r0, 
  "users"."name"        AS t0_r1, 
  "users"."created_at"  AS t0_r2, 
  "users"."updated_at"  AS t0_r3, 
  "orders"."id"         AS t1_r0, 
  "orders"."cost"       AS t1_r1, 
  "orders"."user_id"    AS t1_r2, 
  "orders"."created_at" AS t1_r3, 
  "orders"."updated_at" AS t1_r4 
FROM "users" 
LEFT OUTER JOIN "orders" 
ON "orders"."user_id" = "users"."id" 
WHERE  ( orders.cost < 20 ) 

我知道这.includes只是两种方法的包装:eager_loadpreload.我知道,因为我上面的查询是在连接表上进行过滤(orders在这个例子中),所以includes很聪明并且知道选择eager_load实现,preload因为preload无法处理这个查询,因为preload没有连接表.

这是我困惑的地方.好的:所以在上面的查询中:引擎盖includes将利用eager_load实现.但请注意当我明确地使用eager_load这个相同的查询时(这includes本质上是做什么):我不需要使用.references!它运行查询并加载数据就好了.没有错误,也没有弃用警告:

# did not specify .references(:orders), and yet no error and no deprecation warning 
users = User.eager_load(:orders).where("Orders.cost < ?", 20)

它执行完全相同的过程没有问题:

SELECT "users"."id"     AS t0_r0, 
  "users"."name"        AS t0_r1, 
  "users"."created_at"  AS t0_r2, 
  "users"."updated_at"  AS t0_r3, 
  "orders"."id"         AS t1_r0, 
  "orders"."cost"       AS t1_r1, 
  "orders"."user_id"    AS t1_r2, 
  "orders"."created_at" AS t1_r3, 
  "orders"."updated_at" AS t1_r4 
FROM "users" 
LEFT OUTER JOIN "orders" 
ON "orders"."user_id" = "users"."id" 
WHERE  ( orders.cost < 20 ) 

这看起来很奇怪.为什么.references需要为includes查询版本指定,.references而不需要为eager_load查询版本指定?我在这里错过了什么?



1> Kristján..:

它归结为他们在弃用警告中提到的问题:

目前,Active Record识别字符串中的表,并且知道将comments表连接到查询,而不是在单独的查询中加载注释.但是,在不编写完整的SQL解析器的情况下执行此操作本身就存在缺陷.由于我们不想编写SQL解析器,因此我们将删除此功能.

在旧版本中,Rails的尝试是有关选择使用查询模式有帮助的,includes会使用preload的策略,如果它可以,但切换到eager_load策略时,它看起来就像你在引用连接表的东西.但是,如果没有一个完整的SQL语法分析程序搞清楚什么表实际上是引用,它像一个正则表达式解析XHTML -你可以得到一些事情做好,但Rails的不能在任何情况下正确的决定.考虑:

User.includes(:orders).where("Orders.cost < 20")

这是一个很好的简单示例,Rails可以告诉您需要Orders加入.现在尝试这个:

User.includes(:orders).where("id IN (select user_id from Orders where Orders.cost < 20)")

这给出了相同的结果,但子查询使得连接Orders不必要.这是一个人为的例子,我不知道是否会Rails的决定加入或不需要第二次查询,但问题是有一些情况启发式可能做出错误的决定.在这种情况下,无论是Rails的将执行不必要的加入,燃烧的内存和拖慢查询,或不进行必要的加入,导致错误.

开发人员决定只询问程序员是否需要加入,而不是使用非常糟糕的故障情况来维护启发式.你能够正确往往比Rails可以(希望)得到它,当你弄错了,很明显要改变什么.

而不是添加references你可以切换到eager_load,但保持includesreferences分离允许其查询模式的实现灵活性.您可以想象.includes(:orders, :addresses).references(:orders)addresses加载了第二种preload查询,因为在连接期间不需要它(尽管Rails实际上只包括addresses在连接中).您不需要指定references何时使用,eager_load因为eager_load 始终加入,preload 总是进行多次查询.所有这些references都是指示includes使用必要的eager_load策略并指定需要哪些表.

推荐阅读
mobiledu2402852357
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有