![](/img/trans.png)
[英]Ruby on Rails: Way to test for ActiveRecord Where conditions without hitting database?
[英]Why is Rails ActiveRecord hitting the database twice?
@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是1对多关系)。 请注意,这是不完整的,我只包括建立关系的行
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
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将执行主sql + broker
sql。 既然你试图获得一排就没有多少收益。 当您尝试访问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")
:joins
选项只是使活动记录为查询添加一个join子句。 它实际上并不使活动记录对已返回的行执行任何操作。 未加载关联,因此访问它会触发查询
:include
选项是关于提前加载关联的全部内容。 活动记录有两种策略可以做到这一点。 一个是通过一个大的连接查询,一个是通过每个关联触发一个查询。 默认值是后者,这就是您看到两个查询的原因。
在rails 3.x上,您可以通过执行Integration.preload(:broker)
或Integration.eager_graph(:broker)
来决定您需要哪些策略。
rails 2.x中没有这样的工具,所以你唯一能做的就是欺骗用于确定策略的启发式方法。 每当rails认为order子句,select子句或条件引用包含的关联上的列时,它就会切换到连接策略(因为它是唯一一个在这种情况下工作的策略)。
例如做类似的事情
Integration.first(:conditions => {...}, :include => :broker, :select => 'brokers.id as ignored')
应强制备用策略(在这种情况下,活动记录实际上忽略了选择选项)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.