아직 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로 작성해봐야겠다
typeORM에 subquery() 메서드가 있는 줄 몰랐네요! 블로그 감사합니다. ㅎㅎㅎ