SubQuery, JPAExpressions
사용하는 때
1. 복잡한 조건을 사용해야 할 때
메인 쿼리에서 사용하기 어려운 복잡한 조건을 처리하기 위해
예) 특정 조건에 맞는 데이터만 필터링해야 할 때
2. 집계 함수와 함께 사용할 때
집계된 데이터를 메인 쿼리에서 다시 처리하기 위해
예) 특정 그룹의 최대값, 최소값, 평균값 등을 구하고 이를 메인 쿼리에서 사용
3. 존재 유무 확인
특정 조건에 맞는 데이터가 존재하는지 확인할 때
예) 특정 조건을 만족하는 레코드가 존재하는 경우에만 데이터를 조회할 때
4. 순위 및 순번 지정
특정 순위나 순번을 지정하기 위해
예) 각 그룹 내에서 순위를 매기거나, 순번을 지정할 때
예시
1. 복잡한 조건을 처리하기 위한 서브쿼리
가장 최근 주문 날짜를 가진 고객을 조회하는 경우
SELECT * FROM Customer c
WHERE c.order_date = (SELECT MAX(order_date) FROM Orders o WHERE o.customer_id = c.id);
2. 집계 함수와 함께 사용
각 부서의 평균 급여보다 높은 급여를 받는 직원 조회
SELECT * FROM Employee e
WHERE e.salary > (SELECT AVG(salary) FROM Employee WHERE department_id = e.department_id);
3. 존재 유무 확인
주문이 있는 고객 조회
SELECT * FROM Customer c
WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.customer_id = c.id);
4. 순위 및 순번 지정
각 부서에서 급여 순위가 높은 직원 조회
SELECT * FROM Employee e1
WHERE salary > (
SELECT AVG(salary)
FROM Employee e2
WHERE e1.department_id = e2.department_id
);
QueryDSL에서의 서브쿼리 사용 예시
1. 복잡한 조건을 처리하기 위한 서브쿼리 - 가장 큰 나이를 가진 멤버 찾기
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(
JPAExpressions
.select(memberSub.age.max())
.from(memberSub)
))
.fetch();
2. 집계 함수와 함께 사용
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.salary.gt(
JPAExpressions
.select(memberSub.salary.avg())
.from(memberSub)
.where(memberSub.department.id.eq(member.department.id))
))
.fetch();
3. 존재 유무 확인
QOrder order = QOrder.order;
List<Customer> result = queryFactory
.selectFrom(customer)
.where(
JPAExpressions
.selectOne()
.from(order)
.where(order.customer.id.eq(customer.id))
.exists()
)
.fetch();
4. 순위 및 순번 지정
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.salary.gt(
JPAExpressions
.select(memberSub.salary.avg())
.from(memberSub)
.where(memberSub.department.id.eq(member.department.id))
))
.fetch();
JPAExpressions: 서브쿼리를 작성하는 데 사용되는 도구
서브 쿼리 주요 메서드
- select: 서브쿼리를 시작하는 데 사용됩니다.
- selectOne: SELECT 1과 같이 단일 상수를 반환하는 서브쿼리를 작성합니다.
- selectFrom: 특정 테이블 또는 엔티티를 대상으로 서브쿼리를 작성합니다.
- exists: 존재 여부를 확인하는 서브쿼리를 작성합니다.
- notExists: 존재하지 않음을 확인하는 서브쿼리를 작성합니다.
JPA JPQL 서브쿼리의 한계
from 절에서 사용 불가
- Hibernate 구현체를 사용하면 Hibernate 에서 지원해주기 때문에 select 절에서 사용이 가능한 것
해결 방안
- 서브쿼리를 join 으로 변경(불가능할 때도 있으나 가능한 경우가 많음)
- 애플리케이션에서 쿼리를 2번 분리해서 실행한다
- 정 안 될때는 nativeSQL 사용할 것