ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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"


    댓글