2012-01-12 4 views
7

복잡한 데이터베이스 모델을 장고에서 설정하고 필터 데이터를 기반으로 여러 계산을 수행해야합니다. 나는 Test 오브젝트, TestAttempt 오브젝트 및 UserProfile 오브젝트 (테스트 할 외부 키와 다시 userprofile 로의 외래 키 포함)를 가지고 있습니다. 테스트 점수를 계산하는 TestAttempt에서 실행하는 방법이 있습니다 (각 테스트와 관련된 정답과 비교하여 사용자가 제공 한 선택 항목 수에 따라 다름). 그리고 각각의 TestAttempt에 따라 평균 테스트 점수를 계산하는 Test에서 실행하는 또 다른 방법입니다.하지만 때로는 특정 세트와 연결된 TestAttempt의 제공된 하위 집합을 기준으로 평균을 원합니다. UserProfiles입니다. 따라서 특정 테스트에 대한 평균 테스트 점수를 다음과 같이 계산하는 대신에 :django의 효율성 쿼리 세트에 대한 __in 조회

[x.score() for x in self.test_attempts.all()] 

이러한 평균값을 계산하면됩니다. 이 같은 쿼리를 수행

user_id_list 내가 목록의 형태로 평균 시험 점수를 발견하고자하는 사용자 프로필 아이디의 특정 부분 집합이다
[x.score() for x in self.test_attempts.filter(profile__id__in=user_id_list).all()] 

. 내 질문은 이것입니다 : user_id_list 참으로 UserProfile의 전체 집합 (따라서 필터는 self.test_attempts.all() 같은 반환합니다) 및 대부분의 시간이 사건이 될 것입니다,이 경우를 확인하기 위해 지불합니까, 그렇다면 필터를 전혀 실행하지 않습니까? 또는 __in 조회가 효율적이기 때문에 user_id_list에 모든 사용자가 포함되어 있어도 필터를 실행하는 것이 더 효율적입니다. 또한 결과 test_attempts를 distinct()로 만드는 것에 대해 걱정할 필요가 있습니까? 아니면 내 쿼리 세트의 구조로 중복을 표시 할 수 없습니까?

편집 : 필터와

SELECT "mc_grades_testattempt"."id", "mc_grades_testattempt"."date", 
"mc_grades_testattempt"."test_id", "mc_grades_testattempt"."student_id" FROM 
"mc_grades_testattempt" WHERE "mc_grades_testattempt"."test_id" = 1 

이 :

SELECT "mc_grades_testattempt"."id", "mc_grades_testattempt"."date", 
"mc_grades_testattempt"."test_id", "mc_grades_testattempt"."student_id" FROM 
"mc_grades_testattempt" INNER JOIN "mc_grades_userprofile" ON 
("mc_grades_testattempt"."student_id" = "mc_grades_userprofile"."id") WHERE 
("mc_grades_testattempt"."test_id" = 1 AND "mc_grades_userprofile"."user_id" IN (1, 2, 3)) 

노트 배열이 원시 SQL 쿼리보고에 관심있는 사람의 경우, 필터를 사용하지 않고 다음과 같습니다 (1,2,3)은 단지 예제 일뿐입니다.

+0

두 경우 모두 생성되는 SQL은 무엇입니까? –

+0

확실하지 않은 경우 특정 쿼리 집합에 대해 SQL을 출력하는 방법은 무엇입니까? 편집, 알아 냈어. 그 순간을 알기 위해 – ecbtln

+0

SQL 쿼리가 추가되었습니다. – ecbtln

답변

2
  1. 짧은 답변은 - 벤치 마크입니다. 여러 상황에서 테스트하고 부하를 측정하십시오. 그것은 최고의 대답이 될 것입니다.

  2. 여기에는 중복 할 수 없습니다.

  3. 두 가지 입체감을 확인하는 것이 실제로 문제가됩니까? 여기에 hypotetic 코드입니다 :

    def average_score(self, user_id_list=None): 
        qset = self.test_attempts.all() 
        if user_id_list is not None: 
         qset = qset.filter(profile__id__in=user_id_list) 
        scores = [x.score() for x in qset] 
        # and compute the average 
    
  4. 내가 할 score 방법을 무엇을 모르는,하지만 당신은 DB 수준에서 평균을 계산할 수없는 이유는 무엇입니까? 그것은 당신에게 훨씬 현저한 성능 향상을 줄 것입니다.

  5. 캐싱에 대해 잊지 마십시오.

2

설명서에 대한 이해에서 모든 쿼리는 실제로 사용되기 전에 작성되었습니다. 예를 들어, test_attempts.all()은 SQL 코드를 한 번 생성하고 쿼리를 실행하면 .count(), for t in test_attempts.all(): 등의 작업을 수행하여 데이터를 얻습니다. 데이터베이스에서 쿼리를 실행하고 Queryset 개체 또는 사용한 경우 Object 만 반환합니다. 도망(). 이를 염두에두고 데이터베이스 호출 횟수는 정확히 같지만 실제 호출은 다를 수 있습니다. 편집 된 게시물에 표시 할 때 원시 쿼리는 다르지만 Django가 데이터에 액세스하기 전에 동일한 방식으로 생성됩니다. Django 관점에서, 그들은 같은 방식으로 생성되고 데이터베이스에서 실행될 것이다. 필자가 생각하기에, all() 상황을 테스트하지 않는 것이 가장 좋습니다. 두 가지 쿼리를 실행하여이를 결정해야합니다. 나는 당신이 가지고있는 코드로 실행해야하고 가장 일반적인 경우로 설명하는 all() 시나리오를 건너 뛴다 고 생각한다. 최신 데이터베이스 엔진은 쿼리를 최적의 순서로 처리하기 때문에 추가 조인이 성능 메트릭을 저해하지 않는 방식으로 쿼리를 실행합니다.

2

사용 Annotation 대신 user_id_list의 모든 항목에 대한 새 데이터베이스 히트를 작성하고 파이썬에서 평균을 수행하는 검색어 세트, 이상 반복.

ms = MyModel.objects.annotate(Avg('some_field')) 
ms[0].avg__some_field # prints the average for that instance 

쿼리 세트의 개체에 대한 특성으로 사용할 수있는 평균으로 설정된 쿼리를 반환합니다. ORM을 사용하면 외래 키 관계를 구조적으로 변경하고 주석 모델을 편리하게 만들 수있는 데이터를 보유 할 모델이 필요합니다. 이 재정렬은 필요한 경우 데이터가 유익한 부작용 (데이터가 특정 방식으로 살아 가기를 좋아 함)을 갖기 때문에 좋은 연습입니다.