I'm maintaining an old rails app written in 2.3(.5 I think). There's an inefficient select that's resulting in the old N+1 problem that I'm trying to fix.
First, a collection of Widg开发者_JAVA技巧ets is loaded with different find_by_sql statements in a switch. After that comes the Widget.association.select that's doing the N+1. I want to replace this select loop with an eager load of Widgets based on the ids of the Widgets from the find_by_sql. I realize I could probably combine these into 1 query, but the find_by_sql are fairly complex, so at this point I'm taking small steps to optimize into hopefully 2 queries.
So basically I have
@widgets = Widget.find_by_sql...
and now I want to use @widgets to do something like
@widgets_and_more = Widget.find(:all, :include => :widget_assoc, :conditions => ["widgets.id IN ", @widgets.ids])
You could use collect
or map
to get an array of the IDs to use in the IN...
part of the query...
@widgets_and_more = Widget.find(:all, :include => :widget_assoc, :conditions => ["widgets.id IN (?)", @widgets.collect(&:id)])
Or, because Rails will deal with it nicely and call .to_param
on each of the objects, you could simply use the array of @widgets
(this is, assuming they are an instance of Widget
and respond to .to_param
obviously)...
@widgets_and_more = Widget.find(:all, :include => :widget_assoc, :conditions => ["widgets.id IN (?)", @widgets])
As you said in your question, tying the two queries together would be a far more graceful way of dealing with this. If you are going to do it this way, you might want to consider getting the first query to select the ids only so at least you're not needlessly storing two lots of the same data in memory.
精彩评论