본문 바로가기
Backend/JPA

[JPQL] 서브 쿼리

by 2245 2023. 12. 24.

출처

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 강의 - 인프런

현업에서 실제로 JPA로 개발을 하고 있습니다. 그런 입장에서보면 지금 작성하고 있는 코드들이 어떻게 작동하는지 이해하는데 큰 도움을 주는 강의입니다. 다음은 제가 느낀 이 강의의 장점들

www.inflearn.com

 

 

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

  • 서브쿼리가 반환하는 값이 있는지를 조사합니다. 
  • 단지, 반환된 행이 있는지 없는지만 검사하고 값이 있으면 참, 없으면 거짓을 반환합니다. 
  1. 한 테이블에 다른 테이블과 외래키(FK)와 같은 관계가 있을 때 유용합니다. 
  2. 조건에 해당하는 ROW의 존재 유무를 판단하면 더 이상 수행하지 않습니다. (지연평가원리-성능이 좋다.)
  3. 일반적으로 SELECT 절까지 가지 않기 때문에 IN에 비해 속도나 성능 면에서 더 좋습니다.
  4. 반대로 조건에 맞지 않는 ROW만 추출하고 싶다면 NOT EXISTS 사용합니다. 
  5. 쿼리 순서: 메인 쿼리 → 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 변경 사항]

 

'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