Rails:即使不需要,也可以在连接上使用.references

时间:2016-01-04 18:28:24

标签: ruby-on-rails ruby

我知道当您使用.references并在联接表上指定# will error out or throw deprecation warning in logs customers = Customer.includes(:orders).where("Orders.cost < ?", 100) 子句时,您应该使用customers

示例:

customers

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

  

Mysql2 ::错误:未知列&#39; Orders.cost&#39;在&#39; where子句&#39;:SELECT   Post.includes(:comments).where("comments.title = 'foo'") 。* FROM config.active_record.disable_implicit_join_references = true WHERE(Orders.cost&lt; 100)

或者您会收到弃用警告:

  

弃权警告:您似乎渴望装载桌子    (字符串SQL中引用的一个:用户,地址)    片段。例如:

includes
     

目前,Active Record识别字符串中的表,并且知道    将评论表加入查询,而不是加载评论    在单独的查询中。然而,这样做没有写完全    SQL解析器本质上是有缺陷的。因为我们不想写SQL    解析器,我们正在删除此功能。从现在开始,你必须明确    当您从字符串引用表时,告诉Active Record:

     

Post.includes(:comments).where(&#34; comments.title =&#39; foo&#39;&#34;)。references(:comments)

     

如果您不依赖隐式联接引用,则可以禁用    功能完全通过设置# No error and no deprecation warning because we are explicitly specifying what table is referenced within that where clause customers = Customer.includes(:orders).where("Orders.cost < ?", 100).references(:orders) 。 (

     

SELECT&#34;用户&#34;。&#34; id&#34; AS t0_r0,&#34;用户&#34;。&#34;名称&#34; AS t0_r1,&#34;用户&#34;。&#34;电子邮件&#34; AS t0_r2,&#34;用户&#34;。&#34; created_at&#34; AS t0_r3,   #&34;用户&#34;&#34;的updated_at&#34; AS t0_r4,           &#34;地址&#34;&#34; ID&#34; AS t1_r0,&#34;地址&#34;。&#34; user_id&#34; AS t1_r1,&#34;地址&#34;。&#34; country&#34; AS t1_r2,&#34;地址&#34;。&#34; street&#34; AS t1_r3,   &#34;地址&#34;&#34; POSTAL_CODE&#34; AS t1_r4,&#34;地址&#34;。&#34;城市&#34; AS t1_r5,   &#34;地址&#34;&#34; created_at&#34; AS t1_r6,&#34;地址&#34;。&#34; updated_at&#34; AS t1_r7    来自&#34;用户&#34;    LEFT OUTER JOIN&#34;地址&#34; ON&#34;地址&#34;。&#34; user_id&#34; =&#34;用户&#34;。&#34; id&#34;    WHERE(addresses.country =&#39;波兰&#39;)

有道理。使用joins时,Rails不希望成为SQL解析器。因此,为了让铁路开心,我们这样做:

where

但是,当您在连接表上使用includes# No error and no deprecation warning. What???? customers = Customer.joins(:orders).where("Orders.cost < ?", 100) 子句时:Rails不会出错,并且rails也不会像includes那样抛出弃用警告:

joins

这对我没有意义。我认为如果rails不想成为references的SQL解析器,那么它也不希望成为customers = Customer.joins(:orders).where("Orders.cost < ?", 100).references(:orders) 的SQL解析器。我认为rails会更喜欢我这样使用references

joins

所以我的问题:

  1. 我错过了什么吗?是否绝对没有为where指定includes并在连接表上使用includes子句,即使joins几乎需要它?如果是这样:为什么referencesjoins之间存在差异?
  2. 我应该在joins前进中指定where吗?也许一旦铁路&#39; sql解析器消失所有那些{{1}}的{​​{1}}子句在连接的表上将不再有效?

2 个答案:

答案 0 :(得分:6)

joinsincludes(两种方法的包装:eager_loadpreload)之间的根本区别在于:

  • joins直截了当地执行SQ​​L并不加选择地返回所有记录
  • includes将确定哪些记录属于哪个模型并构建模型树:author1 -> [post, post, post], author2 -> [post, post]

例如,如果您加入Order.joins(:product_items),您将获得:

  • 所有与订单交织在一起的ProductItem记录。
  • Order.includes(:product_items)只有 Orders的每个相应订单中都嵌套了product_items。

所以这里的reference只有在includes(或eager_loadpreload)帮助区分SQL连接数据时才对Rails有用。将其分配给适当的模型。

使用joins,您可以在SQL世界中独立完成。大多数情况下,我会将其与group("orders.id")和聚合函数一起使用:

select(orders.*, count(product_items.id) as cart_size) ...

答案 1 :(得分:1)

这里的关键区别是includes有两种操作模式:eager_load(每个关联使用一个连接)和preload(为每个关联运行一个单独的查询,因此不支持引用的条件)联合表)

在rails 2(或其附近)之前,唯一的选择是基于连接的选项。当新策略被引入时,它被设为默认策略,如果为了减少人们应用中的回归而看起来需要回退旧策略。

这种检测总是有点混乱,并且有一个永无止境的长尾病例没有检测到,因此最终决定删除回退并要求明确使用references来指示哪些包含的关联在查询的任何子句中使用。您可以在eager_loading?方法中看到这一点,该方法用于决定使用哪种策略。

您还可以使用preloadeager_load代替includes来直接指出您想要的策略(或针对不同的关联使用不同的策略)。

另一方面,joins始终进行连接 - 它从不需要解析您的查询,因为它根本不需要 - 无论如何都会生成连接子句。