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"


+ Random Posts