首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么Rails ActiveRecord两次命中数据库?

为什么Rails ActiveRecord两次命中数据库?
EN

Stack Overflow用户
提问于 2012-06-22 06:49:12
回答 2查看 1.6K关注 0票数 2
代码语言:javascript
复制
@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

结果如下:

代码语言:javascript
复制
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 ->集成是一对多关系)。请注意,这是不完整的,我只包含了建立它们之间关系的行

代码语言:javascript
复制
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

代码语言:javascript
复制
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) 
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-06-22 09:40:24

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

代码语言:javascript
复制
Integration.first(:conditions=>{:integration_name => params[:integration_name]}, 
  :include => :broker)

没有必要给出select子句,因为您并不想规范化brokers表列。

注意事项1:

当急切加载依赖项时,AR为每个依赖项执行一个SQL。在您的例子中,AR将执行main sql + broker sql。由于您试图获得一行,因此不会有太多收益。当您尝试访问N行时,如果您预先加载依赖项,则可以避免N+1问题。

注释2:

在某些情况下,使用自定义的即时加载策略可能是有益的。让我们假设您只想获得集成的相关代理名称。您可以按如下方式优化sql:

代码语言:javascript
复制
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

代码语言:javascript
复制
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")
票数 8
EN

Stack Overflow用户

发布于 2012-06-25 04:21:47

:joins选项只是让活动记录向查询中添加一个join子句。它实际上不会让活动记录对已返回的行执行任何操作。该关联未加载,因此访问它会触发查询

:include选项是关于提前加载关联的。Active record有两种策略来实现这一点。一种是通过大型连接查询,另一种是在每个关联中触发一个查询。默认情况下是后者,这就是为什么您会看到两个查询。

在Rails3.x上,您可以通过执行Integration.preload(:broker)Integration.eager_graph(:broker)来决定您想要使用哪种策略。

在Rails2.x中没有这样的功能,所以你唯一能做的就是欺骗用来确定策略的启发式方法。每当rails认为order子句、select子句或条件引用了包含的关联上的列时,它就会切换到联接策略(因为它是唯一在这种情况下有效的策略)。

例如,做像这样的事情

代码语言:javascript
复制
Integration.first(:conditions => {...}, :include => :broker, :select => 'brokers.id as ignored')

应该强制使用备用策略(在这种情况下,活动记录实际上会忽略select选项)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11147942

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档