sql - 为什么 Rails ActiveRecord 访问数据库两次?

标签 sql ruby-on-rails activerecord

@integration = Integration.first(:conditions=> {:integration_name => params[:integration_name]}, :joins => :broker, :select => ['`integrations`.*, `brokers`.*'])
$stderr.puts @integration.broker.id # This line causes Brokers to be queried again

结果:

Integration Load (0.4ms)   SELECT `integrations`.*, `brokers`.* FROM `integrations` INNER JOIN `brokers` ON `brokers`.id = `integrations`.broker_id WHERE (`integrations`.`integration_name` = 'chicke') LIMIT 1
Integration Columns (1.5ms)   SHOW FIELDS FROM `integrations`
Broker Columns (1.6ms)   SHOW FIELDS FROM `brokers`
Broker Load (0.3ms)   SELECT * FROM `brokers` WHERE (`brokers`.`id` = 1) 

为什么 Rails 会再次为 brokers 访问数据库,即使我已经加入/选择了他们,你知道吗?

这是模型(Broker -> Integration 是一对多关系)。请注意,这是不完整的,我只包含了建立它们关系的行

class Broker < ActiveRecord::Base

  # ActiveRecord Associations
  has_many :integrations

class Integration < ActiveRecord::Base

  belongs_to :broker

我使用的是 Rails/ActiveRecord 2.3.14,所以请记住这一点。

当我执行 Integration.first(:conditions=> {:integration_name => params[:integration_name]}, :include => :broker) 时,该行导致两个 SELECTs

Integration Load (0.6ms)   SELECT * FROM `integrations` WHERE (`integrations`.`integration_name` = 'chicke') LIMIT 1
  Integration Columns (2.4ms)   SHOW FIELDS FROM `integrations`
  Broker Columns (1.9ms)   SHOW FIELDS FROM `brokers`
  Broker Load (0.3ms)   SELECT * FROM `brokers` WHERE (`brokers`.`id` = 1) 

最佳答案

使用 include 而不是 joins 来避免重新加载 Broker 对象。

Integration.first(:conditions=>{:integration_name => params[:integration_name]}, 
  :include => :broker)

没有必要提供 select 子句,因为您没有尝试规范化 brokers 表列。

注1:

在预先加载依赖项时,AR 为每个依赖项执行一个 SQL。在您的情况下,AR 将执行 main sql + broker sql。由于您试图获得一行,因此 yield 不大。当您尝试访问 N 行时,如果您预先加载依赖项,您将避免 N+1 问题。

注2:

在某些情况下,使用自定义预加载策略可能会有所帮助。假设您只想获取集成的关联代理名称。您可以按如下方式优化您的 sql:

integration = Integration.first(
  :select => "integrations.*, brokers.name broker_name",
  :conditions=>{:integration_name => params[:integration_name]}, 
  :joins => :broker)

integration.broker_name # prints the broker name

查询返回的对象将包含 select 子句中的所有别名列。

当您想要返回 Integration 对象时,即使没有相应的 Broker 对象,上述解决方案也不起作用。您必须使用 OUTER JOIN

integration = Integration.first(
  :select => "integrations.*, brokers.name broker_name",
  :conditions=>{:integration_name => params[:integration_name]}, 
  :joins => "LEFT OUTER JOIN brokers ON brokers.integration_id = integrations.id")

关于sql - 为什么 Rails ActiveRecord 访问数据库两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11147942/

相关文章:

ruby-on-rails - 远期到期日

mysql - Rails - 用于创建用户组的 Controller 问题

mysql - 多个分组依据的聚合函数

使用 MySQL 的 PHP 有时会返回错误,有时会返回错误,尽管我什么也没做

ruby-on-rails - 如何在没有 3rd 方工具的情况下生成网站缩略图?

ruby-on-rails - Rails/ActiveRecord:避免两个线程同时使用锁更新模型

ruby - ActiveRecord 不会更新

mysql - 如何在 mysql 中将项目分配给客户?

php - Laravel -> 根据模型的方法结果进行查询

ruby-on-rails - Rails 5 ActiveRecord 查询 - 可以加入 3 个表吗?