Active Record查询性能对比:单次DB查询后过滤vs两次DB查询
好问题!咱们来仔细拆解这两种查询方式的性能差异,以及你的判断是否准确,最后再聊聊OR查询的效率问题。
一、你的判断是否正确?
首先要明确:ActiveRecord的Relation对象是延迟加载的——只有当你调用pluck、to_a、first等方法时,才会真正执行数据库查询。
来看你的两种方式:
- 方式一:两次独立的
pluck调用,会触发2次数据库查询,分别获取made和taken的金额数据,然后在Ruby端求和。 - 方式二:默认情况下,
made_and_taken是一个未执行的Relation对象。当你调用made_and_taken.where(...).pluck时,会在原OR条件基础上追加新的WHERE条件,生成新的SQL并执行——也就是说,两次pluck会触发2次数据库查询,和方式一的查询次数完全一样,性能不会有明显差异。
你的判断“后续的where子句不会访问数据库”只有在把数据加载到内存后才成立。比如修改方式二:
# 先把符合OR条件的记录加载到内存(触发1次数据库查询) made_and_taken = SingleBet.where(maker_id: @current_user.id, taker_id: user.id, status: 'accepted') .or(SingleBet.where(taker_id: @current_user.id, maker_id: user.id, status: 'accepted')) .load # 后续的过滤在内存集合中完成,不再触发数据库查询 @total = made_and_taken.select { |bet| bet.maker_id == @current_user.id }.sum(&:amount) + made_and_taken.select { |bet| bet.taker_id == @current_user.id }.sum(&:taker_amount)
这种情况下,方式二只需要1次数据库查询,确实比方式一更快。
二、使用OR的Active Record查询是否更高效?
这取决于你的使用场景:
- 如果需要一次性获取符合多个OR条件的记录:使用
or方法可以减少数据库查询次数(比如原本要查2次的,现在1次搞定),这时候是更高效的。 - 如果后续还要拆分过滤:像你原本的方式二那样,基于OR结果再次拆分查询,
or不仅没带来效率提升,反而可能让SQL语句更复杂,甚至影响索引的使用(OR条件对索引的利用不如AND友好)。
三、更优的方案:让数据库直接计算总和
其实还有一种性能更好的方式——把求和逻辑放到数据库端完成,只需要1次查询,不需要把数据传输到Ruby端再计算:
@total = SingleBet.where(status: 'accepted') .where( SingleBet.arel_table[:maker_id].eq(@current_user.id).and(SingleBet.arel_table[:taker_id].eq(user.id)). or(SingleBet.arel_table[:taker_id].eq(@current_user.id).and(SingleBet.arel_table[:maker_id].eq(user.id))) ) .select( "SUM(CASE WHEN maker_id = ? THEN amount ELSE 0 END) + SUM(CASE WHEN taker_id = ? THEN taker_amount ELSE 0 END) AS total", @current_user.id, @current_user.id ) .first&.total.to_i
这种方式利用SQL的CASE语句在数据库中直接计算总和,性能是三种方式里最好的,尤其当数据量较大时优势更明显。
内容的提问来源于stack exchange,提问作者slindsey3000
相关产品推荐
相关产品推荐

