데브코스 최종 프로젝트중 프론트분이 여행지들 조회에서
총 리뷰수(reviewCount)와, 총 찜한수(saveCount) 가
여행지 상세 조회랑 맞지 않는다는 연락이 왔다
여행지들 조회시

id가 25번인 창경궁이 총 리뷰수는 12개, 총 찜한 횟수도 12개 라고 나온다
실제 DB에 저장된 데이터를 확인해보자
리뷰 테이블
리뷰 갯수는 총 6개

찜 테이블
찜한 횟수는 총 2개

DB에 저장되어 있는 데이터와 맞지않다...
왜 리뷰수 와 찜 테이블이 곱셈이 되서 나오는 걸까
쿼리문을 확인해 보자
const query = this.placeRepository
.createQueryBuilder('place')
.leftJoin('place.rating', 'rating')
.leftJoin('place.carts', 'cart')
.select([
'place.id',
'place.address AS address',
'place.image AS image',
'place.title AS title',
'place.mapx AS mapx',
'place.mapy AS mapy',
'place.viewCount AS viewCount',
])
.addSelect('AVG(rating.ratingValue)', 'averageRating')
.addSelect('COUNT(rating.placeId)', 'reviewCount')
.addSelect('COUNT(cart.placeId)', 'saveCount')
.skip((searchOption.page - 1) * searchOption.limit)
.take(searchOption.limit)
.groupBy('place.id');
리뷰 테이블(rating) 과 찜 테이블(cart) 두 테이블을 조인하고
리뷰 테이블의 placeId 갯수와
찜 테이블의 placeId 갯수를 카운트 하는 쿼리문이다
테이블 2개를 조인했을때 결과값이 어떻게 나오는지 확인하기 위해 SQL문을 던져봤다
SELECT place.id AS placeId, cart.cartId AS cartId, place_rating.id AS ratingId
FROM place
LEFT JOIN cart ON cart.placeId = place.id
LEFT JOIN place_rating ON place_rating.placeId = place.id
WHERE place.id = 25
결과...

12개의 행이 반환됬다
그렇다.. 두 테이블을 조인하다 보니 행이 2 * 6 로 12행이 나온것이다
해당 결과값으로 COUNT를 때리니 총리뷰 갯수와, 총 찜한 갯수가 12 라고 나왔던것

그림으로 그려보자면 이런 느낌
해결방법
테이블 2개를 조인하여 중복된 데이터가 발생하므로 DISTINCT로
각각의 테이블 중복을 제거하여 COUNT를 세면된다
SELECT COUNT(DISTINCT cart.cartId) AS saveCount, COUNT(DISTINCT place_rating.id) AS reviewCount
FROM place LEFT JOIN cart ON cart.placeId = place.id
LEFT JOIN place_rating ON place_rating.placeId = place.id
WHERE place.id = 25

찜한 횟수와 리뷰 갯수가 잘 계산되서 나온다
ORM 쿼리 빌더도 수정했다
const query = this.placeRepository
.createQueryBuilder('place')
.leftJoin('place.rating', 'rating')
.leftJoin('place.carts', 'cart')
.select([
'place.id',
'place.address AS address',
'place.image AS image',
'place.title AS title',
'place.mapx AS mapx',
'place.mapy AS mapy',
'place.viewCount AS viewCount',
])
.addSelect('AVG(rating.ratingValue)', 'averageRating')
.addSelect('COUNT(DISTINCT rating.id)', 'reviewCount')
.addSelect('COUNT(DISTINCT cart.cartId)', 'saveCount')
.skip((searchOption.page - 1) * searchOption.limit)
.take(searchOption.limit)
.groupBy('place.id');

ORM도 문제없이 잘 나온다
평균값도 중복 데이터들을 제거한후
점수값들에 대한 평균을 낼까 싶었지만
쿼리문이 복잡해질꺼 같고
평균값은 데이터 중복이 되도 상관없는지라
그대로 진행하도록 했다
하지만 쿼리 성능이 너무 느리다...

여행지 조회하는데 1초가 넘어가 버린다
다음에는 해당 쿼리의 성능을 개선해 봐야겠다
'database' 카테고리의 다른 글
| SQL 쿼리 성능 개선 (0) | 2024.08.19 |
|---|---|
| 시퀄라이즈 모델들 비동기적으로 연결하기 (0) | 2024.04.13 |
| ORM 사용 후기 (0) | 2024.04.11 |
| mariadb Node 환경에서 트랜젝션과 관리 (0) | 2024.04.02 |
| MySQL 자료형 (1) | 2024.03.14 |