DEV ℧ Developer Diary

[JPA] JPQL - 서브쿼리

해당 포스트는 인프런 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 을 듣고 정리한 글입니다.

서브쿼리

JPA의 서브쿼리는 SQL 에서 말하는 서브쿼리와 동일하다. SELECT와 같은 쿼리안에 또다른 쿼리를 넣어 실행시키는 것을 말한다.

SELECT  M.ID
       ,(SELECT T.NAME
           FROM TEAM T
          WHERE M.ID = T.MEMBER_ID) AS TEAM_NAME
FROM MEMBER M

위와 같이 MEMBER쿼리 안에 TEAM의 NAME을 조회하는 쿼리를 집어넣어 데이터를 조회하고 있다.

JPQL의 서브쿼리

  • 나이가 평균보다 많은 회원

select m from Member m where m.age > (select avg(m2.age) from Member m2)

  • 한 건이라도 주문한 고객

select m from Member m where (select count(o) from Order o where m = o.member) > 0

예시

간단하게 스칼라 서브쿼리를 jpql로 구현해보았다.

String subQuery = "select (select avg(m1.age) from Member m1) as avgAge from Member";
List<Object[]> result = entityManager.createQuery(subQuery)
        .getResultList();
/* select
        (select
            avg(m1.age)
        from
            Member m1) as avgAge
    from
        Member */ select
            (select
                avg(cast(member1_.age as double))
            from
                Member member1_) as col_0_0_
        from
            Member member0_

서브 쿼리 지원 함수

  • [NOT] EXISTS (subquery): 서브쿼리에 결과가 존재하면 참
    • ALL, ANY, SOME (subquery)
    • ALL : 모두 만족하면 참
    • ANY, SOME: 같은 의미, 조건을 하나라도 만족하면 참
  • ALL의 예시

팀A 소속인 회원 : select m from Member m where exists (select t from m.team t where t.name = ‘팀A’)

  • ANY의 예시

전체 상품 각각의 재고보다 주문량이 많은 주문들 : select o from Order o where o.orderAmount > ALL (select p.stockAmount from Product p)

  • SOME의 예시

어떤 팀이든 팀에 소속된 회원 : select m from Member m where m.team = ANY (select t from Team t)

  • [NOT] IN (subquery): 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참

한계

  • JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능
  • SELECT 절도 가능(하이버네이트에서 지원)
  • FROM 절의 서브 쿼리는 현재 JPQL에서 불가능
    • 조인으로 풀 수 있으면 풀어서 해결
    • 차선책으로는 네이티브 쿼리 또는 쿼리를 N번 날리는 방법이 있다.
    • 하지만 네이티브 쿼리와 같은 경우 View에서 원하는 데이터 타입을 맞추기가 어렵다.