我正在尝试创建一个非常复杂的查询,但遇到了麻烦 - 所以我要回到基础,试图找出我错过了什么。 我一直在阅读Active Record Associations
和Active Record Query Interface
的Rails Guides
(特别是第 12 节 - 连接),但我不明白它们是如何相关的以及为什么需要连接/包含。
关联页面显示"通过Active Record关联,我们可以通过声明性地告诉Rails两个模型之间存在连接来简化这些和其他操作。 "查询"页的第 12.2 节指出"Active Record 允许您使用模型上定义的关联的名称作为快捷方式,以便在使用 joins 方法时为这些关联指定 JOIN 子句。
在我看来,这两种说法似乎有些不一致。 如果我创建为belongs_to关联,如果我尝试从两个表中提取数据,为什么需要联接? 换个角度看:
class Customer < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :customer
end
如果我这样做@orders = Order.all
我可以通过执行@orders.first.customer.name
来输出客户名称。 但是,如果我想选择名称中带有"smith"的所有订单,我会做类似@orders=Order.where('customer.name ilike "%smith%"').joins(:customer)
为什么这种"关系"在前半段起作用,但在后半段需要加入?
您不需要加入,但是在您调用关联之前,不会加载您的数据。
这是一种称为延迟加载ActiveRecord::Base
质量。
您可以在控制台的 SQL 输出中看到这一点。
user = User.find(1)
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
这个特定的用户模型有超过一百个关联。
为什么没有加载任何内容?
因为我们还没有打电话给他们。
user.articles
Article Load (0.3ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 1
现在我们看到查询已执行。
按顺序,这在使用普通的旧 Ruby 时成为一个问题。
例如,请考虑以下事项:
users.each do |user|
puts user.articles.first.title
end
运行以下代码是有问题的,因为每次 Ruby 迭代用户时,它只为该用户调用文章。
您最终会重复查询执行以下 SQL 的每个用户:
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 1 LIMIT 1
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 2 LIMIT 1
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 3 LIMIT 1
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 4 LIMIT 1
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 5 LIMIT 1
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 6 LIMIT 1
etc.
我们可以通过在单个查询中最初加载所有数据来解决此问题。
users.joins(:articles).each do |user|
puts user.articles.first.title
end
这将在枚举开始之前执行以下 SQL:
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` IN(1, 2, 3, 4, 5, 6, etc.)
这就是includes
和joins
等ActiveRecord::Base
方法发挥作用的地方。
这里有两篇关于此事的好文章:
http://blog.arkency.com/2013/12/rails4-preloading/
https://rubyinrails.com/2014/01/08/what-is-lazy-loading-in-rails/