코드 예시들은 실 사용 코드에서 관련된 내용들만 추려 간략화한 예시들입니다. 

 

특정 테이블 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://simp1e.tistory.com/entry/MYSQL-order-by-%ED%9B%84%EC%97%90-group-%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95

https://mingggu.tistory.com/115

https://stackoverflow.com/questions/54136211/spring-boot-jpa-how-to-find-entity-with-max-value

https://jaenjoy.tistory.com/39

+ Recent posts