본문 바로가기
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