Rails 5 上的 Ruby 中的 includes(:associations).references(:associations) 和 eager_load(:associations) 之间有什么区别吗?

Is there any diferrence between includes(:associations).references(:associations) and eager_load(:associations) in Ruby on Rails 5?

似乎 includes(:associations).references(:associations)eager_load(:associations) 在 Rails 中执行 完全相同的 SQL (LEFT OUTER JOIN) 5.那么什么时候需要使用includes(:associations).references(:associations)语法呢?

例如,

Parent.includes(:children1, :children2).references(:children1).where('(some conditions of children1)')

可以转换为

Parent.eager_load(:children1).preload(:children2).where('(some conditions of children1)')

我认为后者(使用eager_loadpreload查询)更简单,看起来更好。

更新

我在我的环境中发现了一个奇怪的行为 (rails 5.2.4.3)。

即使我 includes 有几个关联并且 references 只有其中一个,所有 我包含的关联都是 LEFT OUTER JOINed。

例如,

Parent.includes(:c1, :c2, :c3).references(:c1).to_sql

执行 SQL,其中 LEFT OUTER JOIN 是 c1、c2、c3 的全部。 我以为它只加入 c1.

的确,includes + references 最终与 eager_load 相同。与 Rails 中的许多事情一样,您可以通过多种方式实现相同的结果,在这里您将亲眼目睹。如果我将它们写在单个语句中,我总是更喜欢 eager_load 因为它更明确并且是单个函数调用。

我也更喜欢 eager_load,因为我认为 references 是一种 hack。它对 SQL 生成器说“嘿,我以一种你不会检测到的方式引用这个对象,所以将它包含在 JOIN 语句中”并且通常在你使用 String 传递 SQL 片段作为查询的一部分。

我唯一一次使用 includes(:associations).references(:associations) 语法是当它是使查询工作所需的工件而不是意图声明时。 Rails Guide 给出了这个很好的例子:

Article.includes(:comments).where("comments.visible = true").references(:comments)

至于为什么引用 1 个关联会导致 3 个关联,我不确定。 includes 的代码使用启发式方法来决定何时使用 JOIN 更快,何时使用 2 个单独的查询更快,第一个查询检索父对象,第二个检索关联对象。我很惊讶地发现 often it is faster to use 2 separate queries。可能是因为查询无论如何都必须使用 1 个连接,算法认为使用 1 个大连接比使用 3 个查询更快,或者它通常认为 1 个连接比 4 个查询快。

我一般不会使用 preload,除非我有充分的理由相信它比 join 快。我会单独使用 includes,让算法决定。