-
[Django] subquery 표현하는 방법 (subquery, outerref 사용법)언어/파이썬 & 장고 2019. 1. 21. 20:59
WHERE 절의 subquery
item = Item.objects.all() base = Base.objects.filter(no__in=Subquery(item.values('no'))) == 동일 쿼리 SELECT * FROM base WHERE no IN (SELECT no FROM item)
SELECT 절의 subquery
item = Item.objects.all() base = Base.objects.annotate(no=Subquery(item.values('no'))) == 동일 쿼리 SELECT *, (SELECT no FROM item) AS "no" FROM base
더 복잡하게
먼저 위에서 그냥 예시를 들었던 모델은 아래와 같이 정의되었다고 가정합니다.
class Base(models.Model): no = models.BigAutoField(primary_key=True) cnt = models.IntegerField() name = models.CharField(max_length=100) class Items(models.Model): no = models.BigAutoField(primary_key=True) name = models.CharField(max_length=100) base_no = models.ForeignKey(Base, db_column='base_no')
subquery 안에서 외부 테이블의 pk를 비교하여 같을 때만 반환해주는 예시는 아래와 같이 표현할 수 있습니다.
from django.db.models import OuterRef, Subquery item_qs = Items.objects.filter( base_no=OuterRef('pk') ) qs = Base.objects.annotate( item_name=Subquery( item_qs.values('name')[:1] ) )
위의 결과를 쿼리로 변환하면 아래와 같이 의도한대로 나옵니다.
SELECT "item_base"."no", "item_base"."name", (SELECT U0."name" FROM "item_items" U0 WHERE U0."base_no" = ("item_base"."no") LIMIT 1) AS "item_name" FROM "item_base"
만약 비교하고 싶은 컬럼이 pk가 아닌 일반 컬럼일 경우, 일반 컬럼을 작성하여 비교할 수 있습니다.
from item.models.base import Items, Base from django.db.models import OuterRef, Subquery item_qs = Items.objects.filter( base_no=OuterRef('cnt') ) qs = Base.objects.annotate( item_name=Subquery( item_qs.values('name')[:1] ) ) -- 동일 쿼리 SELECT "item_base"."no", "item_base"."cnt", "item_base"."name", (SELECT U0."name" FROM "item_items" U0 WHERE U0."base_no" = ("item_base"."cnt") LIMIT 1) AS "item_name" FROM "item_base"