@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)
时,该行导致两个 SELECT
s
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/