ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • typeorm에서 페이징 처리시 distinct 실행 문제
    NodeJS 2022. 11. 16. 15:10

    typeorm에서 페이징 처리시, distinct 가 실행되는 문제 처리 방법

    다음과 같이 typeorm을 사용하여 페이징 처리를 하였습니다.

    export class CmtCommentDService {
        async getCommentList(searchCommentDto: SearchCommentDto) {
            const dataList = await this.cmtReplyDRoRepository.find({
            order: { id: 'DESC' },
            skip: searchCommentDto.page * searchCommentDto.limit,
            take: searchCommentDto.limit,
            });
            return dataList;
      }
    }
    

    실형 결과를 보면, 아래와 같이 2회 SQL을 질의합니다.

    -- distinct 로 id 조회 처리 
     select
    	distinct `distinctAlias`.`CmtReplyD_id` as ids_CmtReplyD_id
    from
    	(
    	select
    		`CmtReplyD`.`mod_dtm` as `CmtReplyD_mod_dtm`,
    		`CmtReplyD`.`mod_sytm_nm` as `CmtReplyD_mod_sytm_nm`,
    		`CmtReplyD`.`reg_dtm` as `CmtReplyD_reg_dtm`,
    		`CmtReplyD`.`reg_sytm_nm` as `CmtReplyD_reg_sytm_nm`,
    		`CmtReplyD`.`use_yn` as `CmtReplyD_use_yn`,
    		`CmtReplyD`.`id` as `CmtReplyD_id`,
    		`CmtReplyD`.`cmt_comment_d_id` as `CmtReplyD_cmt_comment_d_id`,
    		`CmtReplyD`.`cmt_txt` as `CmtReplyD_cmt_txt`,
    		`CmtReplyD`.`like_cnt` as `CmtReplyD_like_cnt`,
    		`CmtReplyD`.`pet_user_d_id` as `CmtReplyD_pet_user_d_id`,
    		`CmtReplyD`.`pet_ccd` as `CmtReplyD_pet_ccd`
    	from
    		`cmt_reply_d` `CmtReplyD`
    	where
    		`CmtReplyD`.`id` = 10) `distinctAlias`
    order by
    	`CmtReplyD_id` asc
    limit 1
    
    -- 실제 데이터조회 처리 
    select
    	`CmtReplyD`.`mod_dtm` as `CmtReplyD_mod_dtm`,
    	`CmtReplyD`.`mod_sytm_nm` as `CmtReplyD_mod_sytm_nm`,
    	`CmtReplyD`.`reg_dtm` as `CmtReplyD_reg_dtm`,
    	`CmtReplyD`.`reg_sytm_nm` as `CmtReplyD_reg_sytm_nm`,
    	`CmtReplyD`.`use_yn` as `CmtReplyD_use_yn`,
    	`CmtReplyD`.`id` as `CmtReplyD_id`,
    	`CmtReplyD`.`cmt_comment_d_id` as `CmtReplyD_cmt_comment_d_id`,
    	`CmtReplyD`.`cmt_txt` as `CmtReplyD_cmt_txt`,
    	`CmtReplyD`.`like_cnt` as `CmtReplyD_like_cnt`,
    	`CmtReplyD`.`pet_user_d_id` as `CmtReplyD_pet_user_d_id`,
    	`CmtReplyD`.`pet_ccd` as `CmtReplyD_pet_ccd`
    from
    	`cmt_reply_d` `CmtReplyD`
    where
    	( `CmtReplyD`.`id` = 10 )
    	and ( `CmtReplyD`.`id` in (1,2,3,4,5,6,7,8,9,10) )
    

    관련 이슈를 찾아 보니, Typeorm add select distinct in my query. I dont want select distinct. Help!를 확인 할 수 있었습니다.

    TypeORM prefers joins, which makes for better performance when pagination is not used.

    번역하면, TypeORM은 조인을 선호하므로 페이지 매김을 사용하지 않을 때 성능이 향상됩니다. 으로, 페이징 처리시에 releation(조인) 처리를 위해서 사용되는 것으로 판된 됩니다.

    먼저 id 목록을 distinct를 이용하여 중복 없는 id를 조회 하고, 다시 in id값을 조회 처리 하여 실제 페이징 이상의 row를 가져와서 객체를 생성합니다.

    하지만, 함께 일하시는 DBA 분의 의견은 distinct 처리로 인해서 되려 성능이 좋지 않다라는 의견을 주셨습니다. Typeorm add select distinct in my query. I dont want select distinct. Help!이 이슈에서도 비슷한 내용이 많이 있습니다.

    typeorm에서 페이징 처리시 distinct을 회피를 아래와 같이 하였습니다.

    export class CmtCommentDService {
        async getCommentList(searchCommentDto: SearchCommentDto) {
    		const ids = await this.cmtReplyDRoRepository.createQueryBuilder('cmt')
    			.distinct(true).distinctOn(['cmt.id'])
          		.select(['cmt.id'])
    			.orderBy('cmt.id', 'desc')
    			.limit(options.limit).offset(options.page * options.limit)
    			.getMany();
    		
            const dataList = await this.cmtReplyDRoRepository.find({
            order: { id: 'DESC' },
            where: (qb) => {
                   qb.where(``, { ids: ids.map(i => i.id)})
                },
            });
            return dataList;
      }
    }
    

    typeorm에서 제공하는 take, skip를 사용하지 않고 MYSQL(DBMS)에서 제공하는 limit, offset을 사용하여 페이지이 처리를 하면, distinct 호출을 회피가 가능합니다.

    또한 relation이 있을 경우 createQueryBuilder을 이용해서 id를 먼저 조회하는 SQL을 최적화하여 실행하는 방식을 추천 드립니다.

    참고자료

Designed by Tistory.