나이가 평균보다 많은 회원
select m from Member m
where m.age > **(select avg(m2.age) from Member m2)**
상위에서 만든 m을 서브쿼리로 가져오지 않았습니다. 서브쿼리에선 새로 정의한 m2만 사용하고 있습니다.
메인 쿼리와 서브 쿼리는 전혀 관계가 없습니다. 보통 이렇게 짜야 성능이 잘 나옵니다.
한 건이라도 주문한 고객
select m from Member m
where **(select count(o) from Order o where m = o.member)** > 0
여기에서는 m을 서브 쿼리에서 사용하고 있습니다. 이렇게 끌고 오면 성능이 잘 안나옵니다.
핵심은 일반적인 SQL에서 지원하는 서브쿼리가 된다는 것입니다.
[NOT] EXISTS (subquery): 서브쿼리에 결과가 존재하면 참
팀A 소속인 회원
select m from Member m
where **exists** (select t from m.team t where t.name = '팀A')
jpa 에서는 연관관계로 조인을 하면 자동으로 외래키와 매칭되는 테이블의 pk 를 찾아서 설정해줍니다. 서브쿼리의 where 절을 보면 자동으로 쿼리가 나가는 것을 알 수 있습니다.
EXISTS 자세히
{ALL | ANY | SOME} (subquery)
ALL 모두 만족하면 참
전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o
where o.orderAmount > **ALL** (select p.stockAmount from Product p)
ALL 자세히
ANY, SOME: 같은 의미, 조건을 하나라도 만족하면 참
어떤 팀이든 팀에 소속된 회원
select m from Member m
where m.team = **ANY** (select t from Team t)
ANY 자세히
[NOT] IN (subquery): 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참
예시
select **(select avg(m1.age) from Member m1) as avgAge** from Member m join Team t on m.username = t.name"
JPA의 표준 스펙에선 안되지만 보통 하이버네이트를 쓰니 된다고 봐도 됩니다.
조인으로 풀 수 있으면 풀어서 해결
예시
select mm from **(select m.age from Member m)** as mm
//Member에서 age로 만든 테이블을 가져온다는 것입니다.
//만들었다는 표현 보다는 그냥 select m.age from Member m를 from으로 가져왔다고 보면 됩니다.
이게 불가능하다는 것입니다.
만약 조인으로 풀 수 없을 때 서브 쿼리와 본 쿼리를 분해해서 2번 날립니다. 그 후에 애플리케이션에서 조립할 수도 있습니다.
이런 것도 정 안되면 네이티브 sql을 써야합니다.