Post

JPQL 조인과 서브 쿼리





참고



조인

  • JPQL에서도 조인을 사용할 수가 있다.
    • Inner Join
    • Outer Join
    • Self Join
    • Cross Join



  • 야구 선수들을 나타내는 Player 테이블
선수 이름포지션연봉
p1투수450t1
p2포수200t1
p3유격수50t2
p41루수0-
p5투수0-
p6투수0-



  • 야구팀을 나타내는 Team 테이블
팀 이름연봉 합계
t11000
t22000
t3500
t4400
t52500
t61000



Inner Join

1
2
3
4
em.createQuery("SELECT p.name FROM Player p inner join Team t on p.team = t", Object[].class)
    .getResultList();

  • SELECT [조회할 멤버 필드] FROM [좌측 엔티티] (inner) join [우측 엔티티] on [조인 조건];

img-description inner join 결과

  • inner join이라 팀이 있는 애들만 조회된 것을 볼 수 있다.



Outer Join

1
2
3
em.createQuery("SELECT ")



Self Join

1
2
3
4
List<Player> resultList = em.createQuery("SELECT p2 FROM Player p1 JOIN Player p2 ON p1.position = p2.position WHERE p1.name='p1'", Player.class)
        .getResultList();

  • 이름이 p1인 플레이어와 같은 포지션인 선수를 모두 조회해오는 셀프 조인 쿼리이다.

img-description 셀프 조인 결과



Cross Join

1
2
3
4
List<Object[]> resultList = em.createQuery("SELECT p.name, t.name FROM Player p, Team t", Object[].class)
        .getResultList();

  • 이렇게 하면 [모든 플레이어 이름] * [모든 팀 이름]의 크로스 조인 결과를 반환한다.

img-description 크로스 조인 쿼리



Theta Join

1
2
3
4
List<Player> resultList = em.createQuery("select p from Player p, Team t where p.name = t.name", Player.class)
        .getResultList();

  • 연관 관계는 없지만 양쪽 테이블의 조인 조건을 비교 연산자로 표현할 수 있는 조인을 말한다.

img-description 세타 조인 쿼리





서브 쿼리

  • JPQL도 서브 쿼리와 서브 쿼리 함수를 지원한다.
1
2
3
4
List<Team> resultList = em.createQuery("SELECT t1 FROM Team t1 WHERE t1.totalSalary > (SELECT AVG(t2.totalSalary) FROM Team t2)", Team.class)
    .getResultList();

  • 팀 샐러리 평균보다 높은 팀들을 반환한다.
  • 메인 쿼리에서는 t1만 사용하고 서브 쿼리에서는 t2만 사용했다.
    • 메인 쿼리와 서브 쿼리가 전혀 연관이 없다.
    • 이렇게 짜야 성능이 좋다고 한다.

img-description 서브 쿼리 결과


1
2
3
4
List<Team> resultList = em.createQuery("SELECT t FROM Team t WHERE t.totalSalary > (SELECT t.totalSalary FROM Team t WHERE t.name = 't1')", Team.class)
    .getResultList();

  • t1 팀보다 팀 연봉이 높은 팀들을 반환하는 쿼리이다. (where 절로 조건을 넣으면 되지만 마땅한 예제가 떠오르지 않아서..)
  • 메인 쿼리에서 서브 쿼리로 같은 t를 사용한다.
    • 이런 경우 성능이 안좋다고 한다.
    • sql에서도 마찬가지라고 한다.

img-description 서브 쿼리 결과



서브 쿼리 지원 함수

  • sql과 마찬가지로 서브 쿼리 함수가 있다.
함수기능
(NOT) EXISTS서브 쿼리에 결과가 존재하면 true
ALL조건이 모두 만족하면 true
ANY, SOME조건 중 하나라도 만족하면 true
(NOT) IN결과 중 하나라도 같은 것이 있으면 true


EXISTS

1
2
3
4
em.createQuery("SELECT p FROM Player p WHERE EXISTS(SELECT t FROM Team t WHERE p.team = t)", Player.class)
    .getResultList();

  • 팀이 존재하는 플레이어들을 조회하는 쿼리이다.
  • NOT EXISTS를 쓴다면 반대로 팀이 존재하지 않는 플레이어들을 조회하는 쿼리가 된다.

img-description EXISTS 쿼리



ALL, ANY, SOME

1
2
3
4
em.createQuery("SELECT p FROM Player p WHERE p.team = ANY(SELECT t FROM Team t WHERE t.totalSalary = 1000)", Player.class)
    .getResultList();

  • 팀 연봉이 1000인 팀들에 소속된 플레이어들을 조회한다.
  • 팀 연봉이 1000인 팀은 t1과 t6이다.
    • 따라서 서브 쿼리가 반환하는 레코드는 2개가 된다.
    • 이 두 개의 팀 중에 아무거나 해당된다면 true가 된다.
    • t1에 소속한 p1, p2가 조회된다.
  • 만약 ANY가 아닌 ALL을 사용한다면,
    • 서브 쿼리가 반환하는 두 개의 팀 p1과 p2에 모두 만족해야 true가 된다.
    • 어느 플레이어 레코드도 t1과 t6 두 팀에 소속될 순 없다.
    • 따라서 어느 것도 조회되지 않는다.


1
2
3
4
em.createQuery("SELECT p FROM Player p WHERE p.salary > ALL(SELECT t.totalSalary FROM Team t WHERE t.totalSalary < 1000)", Player.class)
    .getResultList();

  • 팀 연봉이 1000보다 낮은 팀들보다 연봉이 높은 선수를 찾는 쿼리이다.
    • 팀 연봉이 1000보다 낮은 팀은 500인 t3와 400인 t4이다.
    • 연봉이 400보다 높은 선수는 연봉이 450인 t1 뿐이다.
    • 하지만 500인 t3보다는 연봉이 낮다.
    • 모든 결과를 만족하지 못했으므로 t1도 조회에서 제외된다.
  • 만약 ANY를 사용했다면 t1은 400인 t4보다 높기 때문에 true가 된다.
1
2
3
4
em.createQuery("SELECT p FROM Player p WHERE p.salary > ALL(SELECT t.totalSalary FROM Team t WHERE t.totalSalary < 500)", Player.class)
    .getResultList();

  • 팀 연봉이 500보다 낮은 팀들로 기준을 낮췄더니 t1이 모든 조건을 만족할 수 있었다.

img-description EXISTS 쿼리



IN

1
2
3
em.createQuery("SELECT p FROM Player p WHERE p.salary IN (450, 350, 200, 100)")

  • 연봉 450, 100에 해당하는 p1과 p2가 조회되는 걸 볼 수 있다.

img-description EXISTS 쿼리



JPA 서브 쿼리의 한계

  • JPA의 JPQL은 WHERE, HAVING 절에서만 서브 쿼리 사용이 가능하다.
    • Hibernate의 JPQL은 SELECT 절에서도 사용 가능.
  • FROM 절에서 서브 쿼리는 현재 JPQL에서 사용 불가능하다.
    • 조인으로 풀 수 있으면 조인으로 푸는게 좋다.
    • 아니라면 네이티브 쿼리를 사용해야 한다.





This post is licensed under CC BY 4.0 by the author.