본문 바로가기
Spring

[Spring] Querydsl 서브 쿼리 적용하기

by 가드 2022. 11. 24.
728x90

JPQL은 FROM절의 서브 쿼리를 지원하지 않는다. JPQL에서 지원하지 않는다는 것은 Querydsl도 동일하게 지원하지 않는다는 것이다.

그래서 서브 쿼리를 충족시키기 위해 Querydsl에 우회 코드를 만들어보자.

SELECT * FROM shop s join (SELECT id FROM shop where id < 100 ORDER BY id LIMIT 10, 10) as tmp on tmp.id = s.id

위의 서브 쿼리 조건을 아래와 같이 Querydsl에 적용하였다.

subQuery

Cluster Key(PK)를 커버링 인덱스로 빠르게 조회한 PK 값들을 Where 절에 적용하여 별도로 필요한 SELECT 칼럼들을 추가 조회하였다.

즉, 위의 쿼리에서는 where, orderBy, limit 절 까지는 커버링 인덱스가 들어간 쿼리로 빠르게 아이디를 조회하고 최종적으로 조회된 10개의 값만을 가지고 추가적으로 조회하는 두 번의 쿼리 방식을 만들면 Querydsl에서 커버링 인덱스를 이용한 서브 쿼리를 만들어 낼 수 있다.

동일한 서브 쿼리를 비교하자면 JDBC 커버링 인덱스 쿼리에 비해 Querydsl 커버링 인덱스 쿼리가 두 번의 네트워크 통신이 발생하지만 전체적으로 비슷한 성능을 보여준다.

하지만 Join으로 해결될 수 있으면 서브 쿼리보다는 Join을 사용하는 것이 성능적인 면에서 좋다. 하지만 서브 쿼리를 대체 못하는 쿼리가 있다.

SubQuery를 Join으로 대체할 수 없는 CASE

1) Group By를 사용한 서브 쿼리가 FROM 절에 있어야 할 때

집계된 값을 구하고자 GROUP BY를 사용한 서브 쿼리가 FROM 절에 들어 있을 때

SELECT product, total_revenue FROM (SELECT product, SUM(revenue) AS total_revenue FROM payment GROUP BY product) AS p

 

2) 집계된 값을 반환하는 서브 쿼리가 WHERE 절에 있어야 할 때

서브 쿼리가 집게 된 하나의 값을 반환하고 그 값을 WHERE 절에서 외부 쿼리의 값과 비교할 때

SELECT product FROM payment WHERE revenue < (SELECT AVG(revenue) FROM payment)

 

3) 서브 쿼리가 ALL 연산자에 있어야 할 때

SELECT product FROM payment WHERE revenue > ALL(SELECT revenue FROM payment)

개인적인 의견

나는 개인적으로는 서브 쿼리 지양한다. 몇 가지 이유가 있는데..

첫 번째는 성능적인 면에서 큰 이득이 취하기 어렵다는 것이다. 서브 쿼리는 View와 마찬가지로 가상 테이블을 만드는 것과 같은데 서브 쿼리가 발생할 때마다 SELECT 구문에 접근하여 데이터를 만들고 이로 인해 연산 비용이 늘어난다. 서브 쿼리가 간단한 경우면 상관없는데 서브 쿼리가 복잡해질수록 연산 비용은 늘어나기 때문이다.

두 번째는 서브 쿼리는 옵티마이저가 쿼리에 접근하고 해설할 때 메타 정보를 얻을 수 없게 된다. 내부적으로 복잡한 연산을 수행하거나 결과 크기가 큰 서브 쿼리를 사용할 때 성능의 문제가 발생될 수 있다.

세 번째는 쿼리의 복잡도이다. 서브 쿼리가 복잡해질수록 해석하기가 쉽지 않다. 복잡하다는 건 가독성이 떨어진다는 것과 같다. 나는 가독성을 무척이나 좋아한다.

나도 서브 쿼리를 사용할 때가 있다. 하지만 서브 쿼리를 적용하기 전에 Join으로 적용 가능한지, Application에서 처리할 수 있는지, 쿼리를 나눠서 실행할 수 있는지 대한 검토를 한 후 성능상 문제가 되지 않는다면 후자를 선택하는 편이고 아니면 최종적으로 서브 쿼리를 사용한다.

300x250

댓글