코드 예시들은 실 사용 코드에서 관련된 내용들만 추려 간략화한 예시들입니다.
특정 테이블 A가 있다고 했을 때 해당 테이블의 칼럼 C2를 group의 기준으로 행들을 뽑아내야했는데 이때 대표로 뽑아지는 행은 각 그룹의 칼럼 C1의 최대값을 가지고있는 행이어야했다. 이 결과값을 다시 C1을 기준으로 오름차순으로 정렬하는 게 필요한 결과 값이었다.
먼저 생각한 로직은 해당 테이블의 행들을 칼럼 C1 내림차순으로 정렬을 하고 그 결과를 C2 칼럼으로 groupby를 한 후 그 결과를 다시 C1 오름차순으로 정렬을 해야되는 로직이었다.
이를 querydsl로 아래와 같이 직관적으로 코드를 짰는데 짜고 실행해보니 제대로 값이 나오지 않았다.
return jpaQueryFactory.selectFrom(A)
.where(A.id.in(JPAExpressions.select(A.id)
.from(A)
.orderBy(A.C1.desc())
.groupBy(A.C2)
))
.orderBy(A.C1.asc())
.fetch();
이후에 orderby와 groupby를 같이 연달아쓰면 안될 것 같다는 생각이 들어 구글링을 하던 중 아래와 같은 쿼리를 발견했다.
select *
from(
select *
from(
select *
from A ud
order by ud.C1 DESC
LIMIT 18446744073709551615
)as ordered
group by ordered.C2 )as grouped
order by grouped.C1;
예전에 부트캠프에서 수업을 들을 때 프로젝트에서 위와 같이 limit를 활용해 orderby와 groupby를 썼던 기억이 있는데 그때와 같은 방법이었다. limit을 지우면 DBMS가 성능 향상을 위해 자동으로 orderby를 무시한다.
-> 성능에 안좋은 쿼리라는 말,,
문제는 그때는 작은 프로젝트라 LIMIT 18446744073709551615를 지정해도 상관없었지만 지금 진행 중인 프로젝트는 규모가 크고 실사용자들이 많이 들어온다. 게다가 해당 쿼리와 연관된 api가 앱 핵심 기능 사용에 있어 필수적인 아주 중요한 api였다.
A 테이블 역시 한 사용자당 데이터양이 끝없이 늘어날 수 있는 데이터의 규모가 테이블들 중 특히 더 사이즈가 큰 테이블이었다.
18446744073709551615라는 숫자가 충분히 큰 숫자인지, 내가 일을 하고 있을 동안엔 문제가 될 것 같지 않은데 해당 서비스가 과거를 돌이켜봤을 때 단순한게 몇 년동안 돌아가고 끝날 서비스가 아닌데 차후에라도 문제가 생긴다면 그건 돈을 받고 개발을 하는 입장에서 잘못된 일이라는 생각이 들었다.
차후에 문제가 생기지않는다하더라도 제한이 없는 코드가 더 좋은 코드라는 생각이 들어 다시 구글링을 통해 아래와 같이 코드를 수정했다.
select A.*
from A inner join
(select max(ud.C1) as updateAt
from A ud
group by ud.C2
) b on A.C1 = b.updateAt
order by A.C1 ASC;
아마 코드를 보면 알겠지만 로직자체가 약간 다르다
원래는 orderby를 한 후 그 결과를 groupby를 하려고했는데 max를 통해 그룹에서 필요한 값을 뽑아냈다.
이 방법이 훨씬 더 깔끔하고 좋은 코드라는 생각이 들었고 성능측면에서도 훨씬 더 좋은 코드인 것 같다.
다른 사람들 코드를 많이 보는 게 중요하다는 걸 다시 한 번 느꼈다..
위 쿼리는 JPQL을 활용해 프로젝트에 적용했다. 위 코드대로 하면 오류가 발생해서 네이티브쿼리로 처리하면 된다는 글을 발견해 네이티브 쿼리로 처리 후 정상 작동 확인했다.
@Query(value=" select A.*\n" +
"from A inner join\n" +
"(select\n" +
"max(ud.C1) as updateAt\n" +
"from A ud\n" +
"group by ud.C2\n" +
") b on A.C1 = b.updateAt\n" +
"order by A.C1 ASC",nativeQuery = true)
List<A> findLatestDevices(@Param("userIdx") Long userId);
참고사이트
https://okky.kr/article/237434
https://mingggu.tistory.com/115
https://stackoverflow.com/questions/54136211/spring-boot-jpa-how-to-find-entity-with-max-value