2011-02-24 3 views
0

나는 2.3로 작성된 오래된 레일 앱을 유지 관리하고있다. 비효율적 인 선택으로 인해 기존 N + 1 문제가 발생합니다.레일 2.3의 조건으로 열심히 부하를 가진 컬렉션을 '다시로드하는 방법'?

먼저 스위치의 다른 find_by_sql 문과 함께 위젯 모음이로드됩니다. 그 후 Widget.association.select가 N + 1을 수행합니다. 이 select 루프를 find_by_sql의 위젯 ID를 기반으로 열심히 위젯로드로 대체하려고합니다. 나는 아마 이것들을 하나의 쿼리로 결합 할 수 있다는 것을 알았지 만, find_by_sql은 상당히 복잡하기 때문에이 시점에서 나는 2 개의 쿼리를 최적화하기위한 작은 단계를 취하고있다.

그래서 기본적으로 나는

@widgets = Widget.find_by_sql...

을 가지고 있고 지금은 ID의 배열을 얻기 위해 당신은 collect 또는 map를 사용할 수

@widgets_and_more = Widget.find(:all, :include => :widget_assoc, :conditions => ["widgets.id IN ", @widgets.ids]) 

답변

0

처럼 뭔가를 @widgets를 사용하려면 쿼리의 IN... 부분에 사용 ...

@widgets_and_more = Widget.find(:all, :include => :widget_assoc, :conditions => ["widgets.id IN (?)", @widgets.collect(&:id)]) 

또는 레일이 멋지게 처리하고 각 객체에 .to_param를 호출하기 때문에, 당신은 단순히 @widgets의 배열을 사용할 수 있습니다 (이 그들이 Widget의 인스턴스이며, 분명히 .to_param에 응답 가정입니다)

@widgets_and_more = Widget.find(:all, :include => :widget_assoc, :conditions => ["widgets.id IN (?)", @widgets]) 

질문에서 말했듯이 두 쿼리를 함께 묶는 것이이 문제를 처리하는 훨씬 더 효과적인 방법입니다. 이런 식으로 할 경우 적어도 첫 번째 쿼리를 통해 ID를 선택하는 것이 좋습니다. 따라서 적어도 두 개의 동일한 데이터가 메모리에 저장되는 것은 불필요합니다.

관련 문제