Published on

Django ORM에서 aggregate, Subquery, OuterRef, Coalesce 쉽게 이해하기

Authors

Django ORM에서 aggregate, Subquery, OuterRef, Coalesce 을 사용하면 어려운 쿼리문을 보다 쉽게 쿼리할 수 있습니다.

하지만 Django ORM 과 SQL 등의 Query 에 익숙치 않다면, 이마저도 사용에 어려움을 느끼기 마련입니다.

이 함수(클래스)들은 집계와 참조를 통해 데이터를 가공할 수 있습니다.

각 코드와 결과 값(타입 포함) 을 잘 생각하면서 사용해야 온전한 목적에 맞게 사용할 수 있는데, 차례대로 살펴보겠습니다.

1. Aggregate

개요: aggregate()는 Django ORM에서 모델 필드의 값에 대해 특정 집계 함수(예: 합계, 평균, 최대, 최소 등)를 적용하고 결과를 반환하는 메서드입니다.

사용법: 이 메서드는 쿼리셋에 대한 집계 연산을 수행하며, 결과는 딕셔너리 형태로 반환됩니다.

예시: 모든 Employee 객체의 평균 급여를 계산합니다.

from django.db.models import Avg
from myapp.models import Employee

average_salary = Employee.objects.aggregate(average_salary=Avg('salary'))

2. Subquery와 OuterRef

2.1. Subquery

개요:Subquery는 한 쿼리 안에서 다른 쿼리를 실행하는 방법으로 사용됩니다.이는 다른 쿼리셋의 결과를 현재 쿼리셋의 필터링, 집계 또는 값으로 사용할 때 유용합니다.

2.2. OuterRef

개요: OuterRef는 외부 쿼리(메인 쿼리)의 필드를 내부 쿼리(서브쿼리)에서 참조하는 데 사용됩니다.

사용법: SubqueryOuterRef를 함께 사용하여 복잡한 쿼리를 구성할 수 있습니다.

이 방법은 특히 관계형 데이터에서 관련된 모델 간의 데이터를 처리할 때 유용합니다.

예시: ParentModel의 각 인스턴스에 대해 연관된 ChildModel 인스턴스들의 value 합계를 계산합니다.

from django.db.models import Subquery, OuterRef, Sum
from myapp.models import ParentModel, ChildModel

# ChildModel의 'value' 필드에 대한 합계를 계산하는 서브쿼리 생성
sum_subquery = Subquery( ChildModel.objects.filter(parent_id=OuterRef('pk')).values('parent_id') .annotate(total_value=Sum('value')) .values('total_value')[:1] ) # ParentModel의 각 인스턴스에 서브쿼리 결과를 포함

parent_queryset = ParentModel.objects.annotate( total_value_of_child=sum_subquery ) # ParentModel 전체에 대한 총 합계를 계산

total_sum = parent_queryset.aggregate(total_sum_of_all_children=Sum('total_value_of_child'))['total_sum_of_all_children']

3. Coalesce

개요: Coalesce는 여러 컬럼이나 표현식 중에서 NULL이 아닌 첫 번째 값을 반환하는 데이터베이스 함수입니다.

Django ORM에서도 Coalesce 함수를 제공하여, 여러 필드 중 NULL이 아닌 첫 번째 값을 선택할 수 있습니다.

사용법: 이 함수는 필드 중 하나 이상이 NULL일 가능성이 있을 때 유용합니다.

특히, 선택적 필드나 기본값이 설정되지 않은 필드에서 NULL 값을 기본값이나 다른 필드의 값으로 대체하고자 할 때 사용됩니다.

예시: field1field2 중 첫 번째 NULL이 아닌 값을 반환합니다.

from django.db.models import Coalesce
from myapp.models import MyModel

queryset = MyModel.objects.annotate( non_null_field=Coalesce('field1', 'field2') )

예시2 : UserProfile모델에서 status 필드에 따라 다른 필드를 우선적으로 선택합니다.

from django.db.models import Case, When, Value, CharField, Coalesce
from .models import UserProfile

user_contact_info = UserProfile.objects.annotate(
    preferred_contact=Case(
        When(status='verified', then=Coalesce('email', 'phone', 'nickname')),
        default=Coalesce('phone', 'email', 'nickname'),
        output_field=CharField()
    )
).values('preferred_contact')

# 결과를 조회합니다.
for user in user_contact_info:
    print(user['preferred_contact'])

hongreat 블로그의 글을 봐주셔서 감사합니다!