[Querydsl] 14. 서브쿼리

민정·2023년 1월 9일

QueryDSL

목록 보기
14/18
post-thumbnail

✨ 서브쿼리

com.querydsl.jpa.JPAExpressions사용

JPAExpressions.select()
                .from()


✨ where절 서브쿼리

✅ 예제 코드 - eq 사용

멤버 중 나이가 가장 많은 멤버들을 조회!

where 절에 서브쿼리를 사용해서 회원 나이 중 최댓값을 조회해온다.

/**
     * 서브 쿼리 - eq 사용
     */
    @Test
    public void subQuery() throws Exception {
        QMember memberSub = new QMember("memberSub");

        List<Member> result = queryFactory
                .selectFrom(member)
                .where(member.age.eq( // 메인 쿼리: 멤버의 나이가 최댓값인 회원 조회
                        // 서브쿼리: member의 나이의 최댓값 조회
                        JPAExpressions
                                .select(memberSub.age.max())
                                .from(memberSub)
                ))
                .fetch();

        assertThat(result).extracting("age")
                .containsExactly(40);

    }

✅ 예제 코드 - goe 사용

멤버 중 나이가 평균값보다 크거나 같은 멤버들을 조회!

where 절에 서브쿼리를 사용해서 회원 나이의 평균 값을 조회해온다.

/**
     * 서브 쿼리 - goe 사용
     */
    @Test
    public void subQueryGoe() throws Exception {
        QMember memberSub = new QMember("memberSub"); // 서브쿼리 테이블명은 메인쿼리 테이블명과 달라야하기때문

        List<Member> result = queryFactory
                .selectFrom(member)
                .where(member.age.goe( // 메인 쿼리: 멤버의 나이가 평균값보다 크거나 같은 멤버 조회
                        // 서브쿼리: member의 나이의 평균값 조회
                        JPAExpressions
                                .select(memberSub.age.avg())
                                .from(memberSub)
                ))
                .fetch();

        assertThat(result).extracting("age")
                .containsExactly(30, 40);

    }

✅ 예제 코드 - in 사용

멤버 중 나이가 10살보다 많은 멤버들을 조회!

where 절에 서브쿼리를 사용해서 10살 보다 많은 회원의 나이들을 조회해온다.

 /**
     * 서브 쿼리 - in
     */
    @Test
    public void subQueryIn() throws Exception {
        QMember memberSub = new QMember("memberSub");

        List<Member> result = queryFactory
                .selectFrom(member)
                .where(member.age.in( // in 사용) 메인 쿼리: 멤버의 나이 10살보다 많은 멤버 조회
                        // 서브쿼리: 10살보다 많은 나이값들 조회
                        JPAExpressions
                                .select(memberSub.age)
                                .from(memberSub)
                                .where(memberSub.age.gt(10))
                ))
                .fetch();

        assertThat(result).extracting("age")
                .containsExactly(20, 30, 40);
    }



✨ select 절 서브쿼리

✅ 예제 코드

멤버의 이름과 유저의 평균 나이를 select


import com.querydsl.jpa.JPAExpressions; // JPAExpression static import

 @Test
    public void selectSubQuery() throws Exception {
        QMember memberSub = new QMember("memberSub");

        List<Tuple> result = queryFactory
                .select(member.username,
                        // 서브 쿼리: 유저 평균 나이 조회, JPAExpressions static import
                        select(memberSub.age.avg())
                                .from(memberSub))
                .from(member)
                .fetch();

        for (Tuple tuple : result) {
            System.out.println("tuple = " + tuple);
        }
    }

✅ 결과


member.username멤버의 평균나이가 같이 select되어 출력되는 것을 확인



✨ from절 서브쿼리

✅ from 절의 서브쿼리 한계 - 중요

JPA 표준 스펙 : where절 서브쿼리만 가능
하이버네이트 구현체 사용 : select 절 서브쿼리 지원

JPA JPQL 서브쿼리의 한계점으로 from절의 서브쿼리는 지원하지 않는다.

✅ from 절의 서브쿼리 해결방안

1번부터 순차적으로 시도

  1. 서브쿼리를 join으로 변경 (불가능할 때도 있음)
  2. 애플리케이션에서 쿼리를 2번 분리해서 실행(이렇게 해도 성능이 괜찮게 나온다면)
  3. nativeSQL 사용(죽어도 from절에 서브쿼리 사용해야할 경우)


✨ 참고할 내용

✅ DB의 역할은 어디까지?

📂 DB

데이터 그룹핑(groupby), 필터링(where..)해서 조회

only 데이터 퍼올리는 것에 집중 -> from절 쿼리 줄일 수 있고, 복잡한 쿼리 많이 줄일 수 있음

💻 애플리케이션, 프레젠테이션

데이터 가공

예) 날짜 이쁘게 포맷하는 건 애플리케이션 단에서 처리

✅ 한방쿼리가 무조건 좋은가?

  • 실시간 트래픽 중요 - 한방 쿼리 선호

    • 쿼리 한방한방이 아까움
  • 백단 관리자(ADMIN) - 한방 쿼리 굳이 선호하지는 않음

    • 좀 느려도 됨.
    • ADMIN 복잡.
    • 복잡하게 쿼리 한방을 날리는 것 보다는 그냥 쿼리를 2,3번 나눠서 날리는게 나을 수도 있음.

참고)
SQL AntiPatterns 책 추천해주셨다!



출처

김영한 강사님 - 인프런 실전! Querydsl

0개의 댓글