출처
https://www.inflearn.com/course/ORM-JPA-Basic/dashboard
목차
Where 서브 쿼리
[예제1] 나이가 평균보다 많은 회원 조회
select m from Member m
where m.age > (select avg(m2.age) from Member m2)
- 상위 쿼리에서 만든 m 을 서브쿼리로 가져오지 않았습니다.
- 서브 쿼리에서는 m2를 새로 정의해서 m2만 사용하고 있는데, 이렇게 메인 쿼리와 서브 쿼리가 전혀 상관이 없게 작성해야 서브쿼리 성능이 잘 나옵니다. (SQL과 마찬가지)
[예제2] 한 건이라도 주문한 고객 조회
select m from Member m
where (select count(o) from Order o where m = o.member) > 0
- 상위에서 만든 m을 서브 쿼리로 가져왔는데, 이 방법은 성능이 잘 나오지 않습니다.
서브 쿼리 지원 함수
- [NOT] EXISTS (subquery): 서브쿼리에 결과가 존재하면 참
- {ALL | ANY | SOME} (subquery)
- ALL 모두 만족하면 참
- ANY, SOME: 같은 의미, 조건을 하나라도 만족하면 참
- [NOT] IN (subquery): 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참
EXISTS
- 서브쿼리가 반환하는 값이 있는지를 조사합니다.
- 단지, 반환된 행이 있는지 없는지만 검사하고 값이 있으면 참, 없으면 거짓을 반환합니다.
- 한 테이블에 다른 테이블과 외래키(FK)와 같은 관계가 있을 때 유용합니다.
- 조건에 해당하는 ROW의 존재 유무를 판단하면 더 이상 수행하지 않습니다. (지연평가원리-성능이 좋다.)
- 일반적으로 SELECT 절까지 가지 않기 때문에 IN에 비해 속도나 성능 면에서 더 좋습니다.
- 반대로 조건에 맞지 않는 ROW만 추출하고 싶다면 NOT EXISTS 사용합니다.
- 쿼리 순서: 메인 쿼리 → EXISTS 쿼리
[예제] 주문 테이블에서 ‘Laptop’을 주문한 고객 정보
CREATE TABLE Customers (
CustomerID INT PRIMARY KEY,
CustomerName VARCHAR(255)
);
CREATE TABLE Orders (
OrderID INT PRIMARY KEY,
CustomerID INT,
ProductName VARCHAR(255),
FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);
SELECT *
FROM Customers
WHERE EXITS {
SELECT 1
FROM Orders
WHERE ProductName = 'Laptop'
AND Orders.CustomerID = Customers.CustomerID
);
- 서브 쿼리 내에서 부모 쿼리의 필드를 사용할 수 있습니다. 마치 Java의 자식 클래스가 부모 클래스의 인스턴스를 사용할 수 있는 것처럼 상속 개념이라 생각하면 됩니다. 이를 연관 서브 쿼리라고 합니다.
- EXISTS 다음으로 오는 SELECT에서 * 대신 아무거나 (1, ‘aa’, …)를 입력해도 상관없습니다.
- EXISTS는 조건에 맞는지에 대한 TRUE/FALSE만 확인하기 때문입니다. 만족하는 결과가 최소 하나가 나오면 바로 TRUE로 판단합니다.
- 즉, Customers 테이블의 CustomerID를 EXISTS 서브 쿼리에 대입했을 때 값이 존재하면, 해당 레코드들을 모아 띄운다 라고 이해하면 됩니다.
참고 : https://inpa.tistory.com/entry/MYSQL-📚-서브쿼리-연산자-EXISTS-총정리-성능-비교
EXISTS 예제
- 팀A 소속인 회원
select m from Member m
where exists (select t from m.team t where t.name = ‘팀A')
ALL 예제
- 전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o
where o.orderAmount > ALL (select p.stockAmount from Product p)
ANY 예제
- 어떤 팀이든 팀에 소속된 회원
select m from Member m
where m.team = ANY (select t from Team t)
JPA 서브 쿼리 한계
- JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용이 가능합니다.
- 단, 하이버네이트에선 SELECT절도 서브 쿼리 사용이 가능합니다.
select (select avg(m.age) from Member m) as avgAge from Member m
- FROM 절의 서브 쿼리는 현재 JPQL에서 불가능합니다.
- 조인으로 풀 수 있으면 풀어서 해결합니다.
selet mm.age, mm.usrename from (select m.age, m.username from Member m) as mm //m.age를 컬럼으로 갖는 테이블
[하이버네이트6 변경 사항]
- 하이버네이트6 부터는 FROM 절의 서브쿼리를 지원합니다.
- 참고 링크 : https://in.relation.to/2022/06/24/hibernate-orm-61-features/
'Backend > JPA' 카테고리의 다른 글
[JPQL] 페치 조인(fetch join) (0) | 2023.12.25 |
---|---|
[JPQL] 경로 표현식 (0) | 2023.12.25 |
[JPQL] 벌크 연산 (0) | 2023.12.24 |
[JPQL] 조인 (0) | 2023.12.24 |
[JPQL] 프로젝션 (0) | 2023.12.24 |