[Dimelo Project] TypeORM querybuilder subquery 사용

Suyeon Pi·2022년 2월 24일
2

Dimelo

목록 보기
9/22

아직 SQL 문법에 익숙하지 않았는 터라 TypeORM 쿼리 짜는 것에 시간이 꽤 걸렸다.

우선 내가 하고 싶었던 쿼리는 해당 강의에 리뷰개수와 리뷰평점을 받아오는 것이었다.

처음 작성했던 잘못된 코드 :

    return this.coursesRepository
      .createQueryBuilder('course')
      .where('course.id =:id', { id })
      .innerJoin('course.Instructor', 'instructor')
      .leftJoin('course.Reviews', 'review')
      .select([
        'course.id',
        'course.title',
        'course.platform',
        'course.price',
        'course.siteUrl AS course_siteUrl',
        `DATE_FORMAT(course.createdAt, '%Y-%m-%d at %h:%i') AS course_createdAt`,
        'instructor.id',
        'instructor.name',
        'COUNT(review.id) AS course_num_review',
        'IFNULL(ROUND(AVG(review.avg),1),0) AS course_avg',
      ])
      .groupBy('review.courseId')
      .getRawOne();
  }

이렇게 했더니 리뷰테이블에 데이터가 하나도 없으면 course데이터가 나오지 않는 것이었다
왜 인가 생각해봤는데 마지막에 groupBy를 review로 했는데 review데이터가 없으니 할 수 었는 거였다. review데이터가 있던 없던 course데이터에는 영향을 안주게 하기 위해서 course와 review를 분리할 필요가 있어보였다..

어떻게 하면 할 수 있지 찾아보다가 subquery라는 것을 알게 되었다.

https://typeorm.biunav.com/en/select-query-builder.html#using-subqueries

SQL문법으로 하면 뭔지 알거 같은데 TypeORM으로 하려니 이해하기 쉽지 않았다.. 특히 중첩하면 대체 이게 뭐지.. SQL을 얕게 공부해서 더 이해하기 어려웠던거 같기도 하고
블로그를 찾다 보니 내 수준에 이해하기 쉬운 글을 발견했다
처음 TypeOrm querybuilder 사용하는데 정말 도움을 많이 받은 블로그 글이다.

https://velog.io/@hkja0111/NestJS-08-TypeORM-QueryBuilder#subquery

이 블로그에서 subquery사용법을 보고 내 코드를 subquery로 고쳐보았다.

const Review = this.reviewsRepository
      .createQueryBuilder()
      .subQuery()
      .select([
        'review.courseId AS courseId',
        'COUNT(review.id) AS num_review',
        'ROUND(AVG(review.avg),1) AS avg',
      ])
      .from(Reviews, 'review') // Entity 이름
      .groupBy('review.courseId')
      .getQuery();

subQuery()를 통해 query안에서 사용할 수 있음을 정의해주고 getQuery()를 통해 query구문인 string형태로 변환해준다. SubQuery에서 alias로 정의한 parameter들은 다른 부모 Query에서도 그대로 사용할 수 있다.

 return this.coursesRepository
      .createQueryBuilder('course')
      .where('course.id =:id', { id })
      .innerJoin('course.Instructor', 'instructor')
      .leftJoin(Review, 'review', 'review.courseId = course.id') // Subquery Review와 조인
      .select([
        'course.id',
        'course.title',
        'course.platform',
        'course.price',
        'course.siteUrl AS course_siteUrl',
        `DATE_FORMAT(course.createdAt, '%Y-%m-%d at %h:%i') AS course_createdAt`,
        'instructor.id',
        'instructor.name',
        'IFNULL(review.num_review,0) AS num_review',
        'IFNULL(review.avg,0) AS course_avg',
      ])
      .getRawOne();
  }

이제 부모 쿼리인 course query에서 아까 만든 review subquery를 leftJoin한다.

이렇게 작성 시 review테이블에 값이 없어도 course query에 더이상 영향을 주지 않았다! 아직 SubQuery의 정의와 종류에 대해서 잘은 모르지만 왜 subquery를 써야하는지 조금은 감을 잡은거 같다.

프로젝트를 끝나고 꼭 SQL공부를 제대로 해서 중첩 subquery로 작성해봐야겠다

profile
Stay hungry, Stay foolish!

1개의 댓글

comment-user-thumbnail
2022년 11월 2일

typeORM에 subquery() 메서드가 있는 줄 몰랐네요! 블로그 감사합니다. ㅎㅎㅎ

답글 달기