Search
📙

#블로그만들기_17 #댓글 목록보기

더미 데이터 만들기

더미 데이터 생성 아래 두 테이블을 join 시키는 연습을 할 것이므로, 원본 데이터 먼저 확인해두자.
insert into reply_tb(comment, board_id, user_id, created_at) values('댓글1', 1, 1, now()); insert into reply_tb(comment, board_id, user_id, created_at) values('댓글2', 4, 1, now()); insert into reply_tb(comment, board_id, user_id, created_at) values('댓글3', 4, 1, now()); insert into reply_tb(comment, board_id, user_id, created_at) values('댓글4', 4, 2, now());
SQL
복사
h2 콘솔 확인 → reply_tb
h2 콘솔 확인 → board_tb

쿼리 연습

이너 조인 (게시글 목록보기 - 댓글도 함께 보이기)

이너 조인은 null값을 표시하지 않는다. 보드ID가 1번과 4번만 댓글을 가지고 있기 떄문에 게시물 2,3번 게시물을 볼수 없다.
select * from board_tb bt inner join reply_tb rt on bt.id=rt.board_id
SQL
복사

outer join연습1 - 필요한 칼럼 뽑아내기

outer join의 특징은 null값도 표시한다. 아래 쿼리문 한글 문장으로 해석 board 테이블을 기준으로 reply테이블을 board_tb의 idreply_tb의 board_id가 같은 값을 가진 행 (row)을 outer join 한다. (값이 없으면 null로 표시→outerjoin) 그 outer join된 값을 bt의 id, title, content, user_id, created_at 와 같이 rt의 id(없으면 0), board_id, comment(없으면 공백) 으로 칼럼을 나열해서 테이블을 만든다.
select bt.id, bt.title, bt.content, bt.user_id, bt.created_at, ifnull(rt.id, 0) rid, rt.board_id, ifnull(rt.comment,'') from board_tb bt left outer join reply_tb rt on bt.id = rt.board_id; // board테이블을 기준으로 reply테이블을 board_tb의 id 와 reply_tb의 board_id가 // 같은 값을 가진 행을 outer join(값이 없으면 null로 표시되는)한다.
SQL
복사
Additional Notes ifnull( a , b) 쿼리 문법 값이 null이 아닐 경우에는 a 값이 null일 경우에는 b 내가 선택한 테이블을 보이게 하고 싶을 때. 아우터 조인 사용 조인의 목적은 두개의 테이블을 다 보여주고 싶을 때 사용

outer join연습2 - 모든 열 표시

쿼리문 한글 해석 board테이블의 idreply_tbboard_id가 같은 행을 board 테이블을 기준으로left outer join 하여 bt의 모든 칼럼과 rt의 모든 칼럼을 표시한다. (null도 표시 (outer join))
select bt.*, rt.* from board_tb bt left outer join reply_tb rt on bt.id = rt.board_id;
SQL
복사

outer join연습3

쿼리문 한글 해석 board 테이블과 reply테이블의 관계에서 bt테이블의 idrt테이블의 board_id의 값이 같은 행 board테이블의 기준으로 (left) outer join 을 하여 테이블을 생성하고 거기서 board테이블의 id 값이 2인 것만 출력한다.
select * from board_tb bt left outer join reply_tb rt on bt.id=rt.board_id where bt.id=2
SQL
복사

outer join (게시글 목록보기 - 댓글 카운트 보이기)

쿼리문 한글 해석 board테이블과 reply테이블의 관계에서 board테이블의 idreply테이블의 board_id가 같은 행을 board테이블을 기준으로 (left)outer join 한다. —> 여기까지는 결과값 6행 group by 함수로 중복되는 값들을 한 행으로 표현한다. [ board테이블의 id, title, content, user_id, created_at ] 여기까지는 다른 board 칼럼들은 한 행으로 표현이 되었는데, 댓글은 여러 개가 있기 때문에 한 행으로 표현할 수 없다. 그래서 한 행으로 표현해 줄수 있는 sum,avg,count같은 연산자가 필요하고 여기서는 count가 사용되었다.
select bt.*, count(rt.id) from board_tb bt left outer join reply_tb rt on bt.id = rt.board_id group by bt.id, bt.title, bt.content, bt.user_id, bt.created_at;
SQL
복사
Group By더 알아보기.. GPT에게 최대한 이해하기 쉽게 답변을 유도하여 정리를 해보았다.
그럼
게시물 1 , 댓글 id = 1 게시물 1 , 댓글 id = 2 게시물 2 , 댓글 없음 게시물 3 , 댓글 id = 1 게시물 3 , 댓글 id = 2 게시물 3 , 댓글 id = 3
SQL
복사
이런 테이블이 있다면
group by 게시글 명령어로 각 게시물의 같은 번호라면 하나로 묶어지고, 나머지 한 게시물에 달린 여러 댓글에는 한 행(row)으로 표현하기 위한 연산이 필요해지는거야?
답변 펼쳐보기
select bt.id, bt.title, bt.content, bt.user_id, but.username, bt.created_at, rt.id r_id, rt.user_id r_user_id, rut.username, rt.comment from board_tb bt left outer join reply_tb rt on bt.id = rt.board_id inner join user_tb but on bt.user_id = but.id left outer join user_tb rut on rt.user_id = rut.id where bt.id = 4;
SQL
복사
쿼리문 한글해석 board테이블과 reply테이블의 관계에서 board테이블의 idreply테이블의 board_id가 같은 행을 board테이블을 기준으로 (left) outer join을 한다. 여기서 얻어진 테이블user테이블의 관계에서 board테이블 user_id user테이블 id 값이 같은 행으로 두 테이블을 붙인다 (inner join) 여기서 얻어진 테이블user테이블의 관계에서 reply테이블의 user_id user테이블의 id가 같은 행을 직전에 얻어진 테이블(inner join된)을 기준으로 (left) outer join한다. 최종적으로 얻어진 테이블에서board테이블 id 4인 값을 조회한후 board테이블 [ id, title, content, user_id, created_at ]reply테이블 [ id, user_id, username, commnet ] 그리고 user 테이블[ username ]칼럼으로 조회한다.

화면 연동하기

화면 먼저 확인

게시글 4번에는 댓글 3개 게시글 3번 2번에는 댓글 없음 게시글 1번에는 댓글 한개가 표시가 되어야된다.
현재 필요한 로직! 화면에 출력할 때, for문을 6번 돌 게 될 것이다. 그러면, 게시글이 6번 출력되지만, 게시글은 4번만 출력되어야 한다. 즉, 위 데이터 행을 DTO에 담고, 전체를 컬렉션에 담으면 화면에 뿌릴때 문제가 된다. 그래서 DB에서 조회된 데이터를 담는 DTO가 있고, 해당 데이터 화면을 위한 DTO에 옮겨 담는 과정이 필요하다.

직접 파싱해서 사용하기

직접 파싱해서 사용하는 원초적 방법! 일단 조회된 내용을 rows에 배열로 다 담는다. 인덱스마다 배열이 담긴 List이다. 따로 담을 그릇들 준비 (boardList, replyList) 저장된 각각의 배열을 인덱스로 모두 따로 꺼내서 옮겨담는다. for each 문 BoardDTO객체를 생성하면서 파싱된 필드값을 옮겨 담는다. 생성된 board객체를 boardList에 추가한다. 조회된 row배열에서 댓글 정보가 저장된 값을 파싱한다. 파싱된 댓글 정보를 replyDTO로 옮겨 담는다. 생성된 reply객체를 replyList에 추가한다. 이렇게 하면 boardList와 replyList는 size가 6인 배열이 생성되었다. 하지만 boardList는 3~5번지가 중복된 값이 들어가있다. (총 0~5번지) 스트림을 이용하여 중복된 값을 합친다. distinct사용 -> 문법 기억하자. boardList를 순회한다. boardDTO에 댓글객체를 ORM매핑한다. 게시글:댓글=1:N 관계 replyList를 순회한다. 순회시에 게시글 번호와 댓글이 참조하는 게시글 번호가 같다면 reply 추가한다.
public List<BoardResponse.BoardDTO> findAllV2() { Query query = em.createNativeQuery("select bt.id, bt.title, bt.content, bt.user_id, bt.created_at, ifnull(rt.id, 0) rid, rt.board_id, ifnull(rt.comment,'') from board_tb bt left outer join reply_tb rt on bt.id = rt.board_id"); // 1. 일단 조회된 내용을 rows에 배열로 다 담는다. 인덱스마다 배열이 담긴 List이다. List<Object[]> rows = (List<Object[]>) query.getResultList(); // 2. 따로 담을 그릇들 List<BoardResponse.BoardDTO> boardList = new ArrayList<>(); List<BoardResponse.ReplyDTO> replyList = new ArrayList<>(); for (Object[] row : rows) { // 3. 저장된 각각의 배열을 인덱스로 모두 따로 꺼내서 옮겨담는다. Integer id = (Integer) row[0]; String title = (String) row[1]; String content = (String) row[2]; Integer userId = (Integer) row[3]; Timestamp createdAt = (Timestamp) row[4]; // 4. BoardDTO를 생성하면서 꺼내놓은 필드값을 옮겨 담는다. BoardResponse.BoardDTO board = new BoardResponse.BoardDTO( id, title, content, userId, createdAt.toLocalDateTime() ); // 5. 생성된 board객체를 boardList에 추가한다. boardList.add(board); // 6. 조회된 row배열에서 댓글 정보가 저장된 값을 파싱한다. Integer rid = (Integer) row[5]; Integer boardId = (Integer) row[6]; String comment = (String) row[7]; // 7. 파싱된 댓글 정보를 replyDTO로 옮겨 담는다. BoardResponse.ReplyDTO reply = new BoardResponse.ReplyDTO( rid, boardId, comment ); // 8. 생성된 reply객체를 replyList에 추가한다. replyList.add(reply); }// 9. 이렇게 하면 boardList와 replyList는 size가 6인 배열이 생성되었다. // 하지만 boardList는 3~5번지가 중복된 값이 들어가있다. (총 0~5번지) // 10. 스트림을 이용하여 중복된 값을 합친다. distinct사용 -> 문법 기억하자. boardList = boardList.stream().distinct().toList(); // 11. boardList를 순회한다. boardDTO에 댓글객체를 ORM매핑한다. 게시글:댓글=1:N 관계 for (BoardResponse.BoardDTO b : boardList){ // 12. replyList를 순회한다. for (BoardResponse.ReplyDTO r : replyList){ // 13. 순회시에 게시글 번호와 댓글이 참조하는 게시글 번호가 같다면 reply 추가한다. if(b.getId() == r.getBoardId()){ b.addReply(r); } } } return boardList; }
SQL
복사
Additional Notes 데이터가 중복되면 장점은??? 조인을 안해도 되어서 속도가 빠르다. 중복이 되어있다면 정렬(군집화)이 되어있어야 된다. 모든 검색의 기본은 풀스캔이다. 풀스캔의 시간 복잡도는 O(n) → 최악의 경우로 계산한다. 중복되는 정보는 distinct로 줄인다. (중복 제거) 추천 DB공부 서적