조회 방식 중 어떤 것이 나은지 모르겠습니다 0 8 1,932

by LHJ [SQL Query] [2023.10.30 01:20:05]


안녕하세요 개인프로젝트로 게시글의 댓글을 조회하는 sql을 짜고있습니다. 

현재 Comment 테이블의 컬럼은 comment_id, content, writer_id, post_id, parent_id 이외의 컬럼이 있고, parent_id는 대댓글일 경우 자신의 부모 댓글 id 가 있고, 부모댓글인 경우는 null입니다. 외래키는 writer_id, post_id, parent_id 입니다. 

select c.comment_id,
       c.content,
       c.comment_status,
       c.user_id,
       c.parent_id,
       c.created_at,
       w.nickname,
       c2.comment_id,
       c2.content,
       c2.comment_status,
       c2.created_at,
       c2.user_id,
       w2.nickname
from comment c
         join user w on c.user_id = w.user_id
         left join comment c2 on c.comment_id = c2.parent_id
         left join user w2 on c2.user_id = w2.user_id
where c.post_id = 1
  and c.parent_id is null
order by c.comment_id,c2.comment_id;

해당 쿼리는 self join으로 부모와 연관된 자식 댓글을 조회하고 writer와 조인 후 조건에 맞는 레코드만 남기고 정렬을 하는 쿼리입니다. 어플리케이션에서 사용하는 라이브러리로 계층형태의 DTO convert가 편하지만 실행 계획을 보면 임시테이블을 생성하고 ,filesort도 일어나게되어 성능하락이 고민되어 생각한 다른 방법은

select c.comment_id,
       c.content,
       c.comment_status,
       c.user_id,
       c.parent_id,
       c.created_at,
       w.nickname
from comment c
         join user w on c.user_id = w.user_id
where c.post_id = 1
order by c.comment_id;

해당 쿼리로 조회해 어플리케이션에서 loop를 돌면서 변환하는 방식입니다. 두가지 방법 중 어떤 것을 선택해야되는지 모르겠어서 질문드립니다. 첫번째 방법같은 경우 쿼리를 개선할 방법이 있다면 조어 부탁드립니다.

by 마농 [2023.10.30 10:32:55]

1. 댓글만 가능한가요? 대댓글은 없는 구조인지?
- 위 쿼리는 대댓글은 나오지 않는 구조네요.
- 하나의 글에 댓글이 여러개 달리면? 원글이 중복되어 나오는 구조이구요.
2. 페이징 처리를 하지 않나요?
- 페이징 처리를 한다면? 조인을 나중에 하는게 낫습니다.
- 다만, 위에서 언급했듯이 댓글이 여러개 달리면? 결과가 달라집니다.
3. 페이징 처리를 어디에서 하나요?
- 어플에서 하는 것 보다는 쿼리에서 하는게 성능에 좋습니다.


by LHJ [2023.10.30 13:20:23]

답변 감사드립니다.

1. 대댓글이 존재하는 구조이고 대댓글의 계층을 1로 제한했기 때문에 parent_id 가 null 이냐 아니냐 여부로 부모 댓글과 대댓글을 구분합니다. 그래서 1번 쿼리로 조회를 하게되면 다음과 같은 결과 나옵니다.

Comment 
comment_id | content| parent_id
        1              "sdf"          null
        2              "sdf"          1
        3              "sdf"          1
        4              "sdf"          null
        5              "sdf"          4
        6              "sdf"          null
부모댓글과 자식 댓글 셀프조인 후

comment_id | content| parent_id | child_id | child_content 
        1              "sdf"          null          2                 "sdf"
        1              "sdf"          null          3                 "sdf"
        4              "sdf"          null          5                 "sdf" 
        6              "sdf"          null        null                null

이렇게 조회 후 spring 어플리케이션에서 사용하는 라이브러리로 아래와 같이 조회하면서 변환합니다.

class Comment {
    Long parentId;
    List<ChildComment> childComments;
}

두가지 쿼리 둘다 어플리케이션에서 변환하기 때문에 차라리 2번 쿼리 방법으로 post_id에 해당하는 comment 만 필터링해서 가져오고 처리하는 방식이 더 낫지 않을까라는 고민이 들어서 질문드렸습니다. 경력도 없는 취준생이어서 구조가 이해가 안되실 수도 있겠네요 ㅜㅜ

2. 페이징 처리는 서비스 성격상 많은 댓글이 달릴거라 생각하지 않아서 아직 고려하지 않고 구현중입니다.!!


by 마농 [2023.10.30 13:33:14]

본글과 댓글이 함께 있는 구조라고 생각햇는데. 댓글만 있는 거네요?
그럼 본글의 ID 는 post_id 인건가요?
특정 본글 하나에 대한 댓글을 조회하는 건데? 느리다구요?
적절한 인덱스만 있다면 느릴 것 같지 않은데요?
대댓글에는 post_id 가 없나요?
어플에서 SQL 조회 결과를 원래의 테이블 형태로 변환하는 기능이 있는 건가요?


by LHJ [2023.10.30 13:45:14]

아 제가 질문을 명확하게 하지 못했나보네요.

프론트의 요구사항으로 게시글 본문을 조회하는 api와 본문의 댓글을 조회하는 api가 따로 있습니다.

말씀해주신대로 본글의 ID가 post_id 맞습니다. 1번 쿼리는 order by 에서 c.comment_id 만 존재하면 실행 계획에서 임시테이블과 filesort가 발생하지 않는데 c2.coment_id를 추가하면 발생하게 됩니다.

어플에서 조회결과를 1번 쿼리로 조회하면 원하는 형태로 자동 변환하지만 2번 쿼리는 결과를 받아서 제가 변환시켜줘야됩니다. 두가지 방법 모두 어플리케이션에서 변환하는 것은 똑같고 자동으로 하냐 제가 직접하냐의 차이입니다.


by 마농 [2023.10.30 13:50:22]

post_id 에 값이 댓글에만 존재하나요?
대댓글에도 똑같이 존재한다면?
셀프조인이나, 어플기능은 불필요해 보입니다.
인덱스는 어떻게 구성되어 있나요?
원본 대비 최종 원하는 결과 샘플 보여주세요.


by LHJ [2023.10.30 14:13:35]
Comment

comment_id | content| parent_id | post_id | writer_id

        1              "sdf"          null          1              1

        2              "sdf"          1              1              2

        3              "sdf"          1              1              1

        4              "sdf"          null          1              3

        5              "sdf"          4              1               1

        6              "sdf"          null          1              4
        7              "sdf"          null          2              3

댓글 대댓글 모두 위와 같이 post_id 가 존재합니다.

 comment_id pk 인덱스, writer_id, parent_id, post_id 가 외래키여서 각각 인덱스가 있습니다.
 

{
   "parentId" : 1,
   "content" : "댓글",
   "writerNickname" : "nickname",
   "childComments" : [
      {
         "childId": 2,
         "content": "대댓글",
         "writerNickname" : "nickname",
      },
      {
         "childId": 3,
         "content": "대댓글",
         "writerNickname" : "nickname",
      }],
   "parentId" : 4,
   "content" : "댓글",
  "writerNickname" : "nickname",
   "childComments" : [
      {
         "childId": 5,
         "content": "대댓글",
         "writerNickname" : "nickname",
      },
   ],
   "parentId" : 6,
   "content" : "댓글",
  "writerNickname" : "nickname",
   "childComments" : [ ]
}

위와 같은 형태로 json 응답을 프론트에게 주고 싶습니다 !!

 

 


by 마농 [2023.10.30 14:27:02]
-- json 형식까지는 아니어도 일단 정렬만 추가해 봤습니다.
SELECT c.comment_id
     , c.content
     , c.comment_status
     , c.user_id
     , c.parent_id
     , c.created_at
     , w.nickname
  FROM comment c
  JOIN user w
    ON c.user_id = w.user_id
 WHERE c.post_id = 1
 ORDER BY IFNULL(c.parent_id, c.comment_id), c.comment_id  -- MySQL
;

 ORDER BY IFNULL(c.parent_id, c.comment_id), c.comment_id  -- MySQL
 ORDER BY ISNULL(c.parent_id, c.comment_id), c.comment_id  -- MSSQL
 ORDER BY NVL(c.parent_id, c.comment_id), c.comment_id  -- Oracle

 


by LHJ [2023.10.30 14:32:38]

답변 정말 감사드립니다 !!

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입