2014-11-20 2 views
1

코드의 성능을 향상시키고 자하는 방법에서이 모델과 다음 행을 사용하고 있습니다. 방법에서루비 - ".each"배열을 통해 루핑 속도를 높이는 방법?

class Location < ActiveRecord::Base 
    belongs_to :company 
end 
class Company < ActiveRecord::Base 
    has_many :locations 
end 

:

locations_company = [] 

### 
found_locations = Location.within(distance, origin: from_result.split(',')).order("distance ASC") 
### 0.002659s 

### 
found_locations.each do |location| 
    locations_company << location.company 
end 
### 45.972285s 

### 
companies = locations_company.uniq{|x| x.id} 
### 0.033029s 

코드는이 기능을 갖는다 - 먼저, 소정 반경 내의 모든 위치를 잡아. 그런 다음 각 행에서 회사를 인수하여 준비된 배열에 저장하십시오. 이것은 문제가되는 부분입니다. 각 루프는 처리하는 데 45 초가 걸립니다.

그런 다음 새로 생성 된 배열에서 중복을 제거하십시오.

이 상황을 해결하기위한 더 좋은 방법이 있을지 궁금하지만 지금 당장은 보지 못한다. 그래서 나는 너희들에게 내가 어떻게 .each과 함께 루핑을 할 수 있는지 물어보고 싶다. 데이터를 배열에 저장하는 것 - 루비에서 객체의 일부 정보를 가져 오는 더 나은 방법이 있습니까?

시간 내 주셔서 대단히 감사합니다. 하루 종일이 문제에 몰입하고 있지만 여전히 효과적인 해결책이 없습니다.

+0

'found_locations'를 보면 가능성이있는 쿼리 프록시이고 통합 된 결과 집합이 아님을 알 수 있습니다. '# each'는 거의 확실하게 병목 현상이 아닙니다. 병목 현상을 찾으려면 코드를 올바르게 프로파일해야합니다. –

+0

이 질문은 리팩토링 및 기존 코드의 성능 향상을위한 것이므로 주제가 아닌 것으로 보입니다. [codereview.se]에 있어야합니다. –

답변

6

가장 좋은 방법은 반복하지 않는 것입니다. 귀하의 최종 목표는 특정 지역의 모든 회사를 찾는 것입니다.

found_locations = Location.within(distance, origin: from_result.split(',')).order("distance ASC") 
companies = Company.where(id: found_locations.pluck(:company_id).uniq) 
+0

db가 지원하는 경우'bleh.uniq' 대신'Company.distinct'가 도움이 될 수 있습니다. – nicooga

+0

Company.distinct는 필요하지 않습니다. uniq를 종료하면 더 큰 배열을'WHERE id IN []'질의로 전달한다. 배열에 ID가 두 번 이상 포함되어 있어도 데이터베이스는 회사 당 하나의 레코드 만 반환합니다.필자는 개인적으로 쿼리보다 더 많은 정보를 필요 이상으로 보내지 않고'uniq'를 추가/제거하는 것이 성능에 실질적으로 영향을주지 않을 것입니다. –

+0

'found_locations'가 회사에 실제로 사용되거나 사용되지 않을지 여부에 따라 다른 변형을 취할 수 있습니다. 만약 당신이'found_locations'를 따로 사용할 계획이라면,'to_a'를 사용하여 그것을 배열에 강제로 넣을 수 있습니다/다음 행의 논리를'Company.where (id : found_locations.map (& : id) .uniq)'. find_locations를 별도로 사용하지 않을 계획이라면, 내가 놓은 것을 최상으로 사용하면 'Location'객체를 생성하지 않고 대신 필요한 ID를 가져올 수 있습니다. –

1

나는 모든 시간이 걸리는 일이 each하지 생각하지만, 오히려 DB에 쿼리.

첫 번째 줄은 실제로 쿼리를 작성하지 않지만 쿼리를 작성합니다.

나는 코드를 작성하는 경우 다음과 같이 있다고 생각 :

locations_company = [] 

found_locations = Location.within(distance, origin: from_result.split(',')).order("distance ASC") 

### this line will take most of the time 
found_locations = found_locations.to_a 
###  

### 
found_locations.each do |location| 
    locations_company << location.company_id 
end 
### 

### 
companies = locations_company.uniq{|x| x.id} 
### 

당신은 each이 훨씬 적은 시간이 걸릴 것입니다 것을 볼 수 있습니다. 쿼리를 최적화해야합니다. 이 관계이기 때문에


@AlexPeachey 아래 주석함에 따라, location.company 또한, 목록의 각 위치에 대한 쿼리를 포함 할 것이다. 당신은 열심히 추가하여 회사를로드 할 수 있습니다

found_locations = Location.includes(:company).within(distance, origin: from_result.split(',')).order("distance ASC") 
+0

쿼리가 느려질 수도 있지만 각 루프를 통해 매번 회사 테이블에 대해 쿼리를 수행하기 때문에이 방법으로 각 쿼리가 즉시 실행되지는 않습니다. 이를 피하기 위해'Location.includes (: company)'로 변경하면 단 하나의 추가 쿼리만으로 필요한 모든 회사가로드됩니다. –

+0

감사합니다 @AlexPeachey, 그 부분을 놓쳤습니다. 답변을 업데이트했습니다. –

1

문제는 각이 아니라 점에서 쿼리는 당신이 그것을 반복 시작할 때 실행을 시작합니다. found_locations은 쿼리 결과가 아니므로 필요할 때 (예 : 결과 반복 실행을 시작할 때) 쿼리를 실행하는 쿼리 작성기입니다.

관련 문제