개발 한 스푼/MySQL

계층형 쿼리

idleday 2023. 8. 17. 16:57

공지사항 게시판에 댓글 기능을 도입한다고 한다.

댓글-대댓글로 2 depth인 줄 알았으나, 대대댓글 3 depth가 될 수도 있고 아닐수도 있고 변경가능성 농후..하므로

일단 3 depth 기준으로 조회 쿼리를 짜기로 했다.

 

 

댓글-대댓글 구조의 테이블 설계는 SQLD 공부할 때 접했던 계층형 쿼리(start with, prior 등) 기능으로 

간단하지 않을까했다.

 

주로 많이 사용하는 기능

  • WITH RECURSIVE 구문
  • CONNECT BY (Oracle)
  • 재귀형 함수 짜기

  

 

Oracle이 아니니까 CONNECT BY PRIOR, START WITH 못써도 괜찮아

 

 

아 니 근 데 

 

WITH RECURSIVE 은 mysql 5.7 이하 미지원인데

SELECT VERSION();
SHOW VARIABLES LIKE '%VERSION%';

innodb_version 이 5.6.36-82.2 이었다..

 

MySQL 버전이 낮아서 재귀 구문을 못쓴다 허허

 

 

허허

 

 

그래서 무식하게 생짜로 짜기로 했다.

댓글 레벨이 3레벨..보다 깊어지는 경우는 생각하지않고 일단 짠다

 

기본로직은 대략 이와 비슷하다

 

 

이어지는 한나절 간의 두뇌 풀가동!

 

 

.

.

.

.

결국 해냈다는 거지..

 

SELECT A.*
FROM comment_table A LEFT OUTER JOIN comment_table B ON A.parentId = B.id -- 부모
WHERE A.boardId = 36 
ORDER BY CASE A.level 
            WHEN 1 THEN A.id 
            WHEN 2 THEN A.pardntId
            WHEN 3 THEN B.parentId
        END
	 , IF(A.level = 3, A.pardntId, A.id)
	 , A.createdDate
;

 

order by 정렬조건 때문에 몇시간 머리 싸맸지만

일단 이걸로 간다..

 

재귀 펑션 활용기는.. 다음에 이어서 해보기로.

 

이제.. 조회쿼리 짰으니 등록/수정/삭제 만들어야지.. 

 

 


+ 2023-08-23 추가

위의 쿼리는.. 댓글이 3depth일때만 사용가능한 매우 제한적인 조건의 쿼리이다.

현업에서 무한댓글을 구현하고자한다.. 

 

그래서 결국 펑션에 손대버렸다.

.

.

.

그렇게 만들게 된 재귀함수 fn_hierarchi()

더보기

#### Function 원리 ####

  

  1. 자신을 부모로 가지는 노드를 찾는다.

  2. 검색된 노드가 없거나 start_with 과 같은 부모노드를 가진다면 해당 노드를 반환한다.

  3. 2번과정이 아니라면 상위 노드로 이동하여 다시 검색한다. 이때 앞에 검색된 id 값보다 증가된 값을 찾는다.

 

DELIMITER $$

USE `myDB`$$

DROP FUNCTION IF EXISTS `fn_hierarchi`$$

CREATE DEFINER=`myDB`@`%` FUNCTION `fn_hierarchi`() RETURNS INT(11)
    READS SQL DATA
BEGIN
	  DECLARE v_id INT;
	  DECLARE v_parent INT;    
	  DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;
	  
	  SET v_parent = @id;
	  SET v_id = -1;
	  
	  IF @id IS NULL THEN
		RETURN NULL;
	  END IF;
	  
	  LOOP
	  
	  SELECT MIN(seqNo)
	    INTO @id
	    FROM t_comment_list
	  WHERE IFNULL(upprSeqNo,0) = v_parent
	    AND seqNo > v_id;
	  
	  IF (@id IS NOT NULL) OR (v_parent = @start_with) THEN 
		SET @level = @level + 1;
		RETURN @id;
	  END IF;
	  
	  SET @level := @level - 1;
	  
	  SELECT seqNo, IFNULL(upprSeqNo,0)
	    INTO v_id, v_parent
	    FROM t_comment_list
	   WHERE seqNo = v_parent;
	   
	   END LOOP;
	
    END$$

DELIMITER ;

 

 

-- 계층형쿼리 실행
SELECT A.seqNo, CASE WHEN fn.lev -1 > 0 THEN CONCAT(CONCAT(REPEAT('  ', fn.lev - 1),'┗'), IF(A.delYn = 'Y','삭제',A.cmnt)) ELSE A.cmnt END AS hierarchi
	 , fn.sort
	, A.*
FROM (	SELECT fn_hierarchi() AS seqNo, @level AS lev, @row:=@row+1 AS sort
	FROM ( SELECT @start_with:=0, @id:=@start_with, @level:=0) vars 
	INNER JOIN t_comment_list A 
	INNER JOIN (SELECT @row:=0 ) B
	WHERE @id IS NOT NULL	
     ) fn INNER JOIN t_comment_list A ON fn.seqNo = A.seqNo AND A.ntcSeqNo = #게시물번호#
WHERE 1=1
ORDER BY fn.sort
;

 

결과물:

 

 

 

 


+ 2023-11-02 추가

와!

 

등록/수정/삭제는 무한 depth 기준으로 구현해야하는데

조회는 2단계로 보여진다한다 마치 유튜브처럼..

 

위 Function으로 인해 대댓글 정렬은 어찌저찌 성공했으나 

삭제여부와 자식댓글이 있는지 유무를 따져 

 

1. 삭제된 댓글인데, 자식이 없다면 → ❌

2. 삭제된 댓글인데, 자식이 있다면 ✅

  2.1 만일 자식이 살아있다면 ✅

  2.2 만일 자식이 삭제됐다면 ✅

    2.21 자식의 자식이 살아있다면 ✅

    2.22 자식의 자식이 삭제됐다면(해당 댓글 밑에 달린 모든 후손_손자,증손 등등_이 삭제됐다면) → ❌

 

1번과 2.22번 저 두 경우를 아예 프론트에 보낼때 제외하고 댓글목록을 정렬해서 보내야했다.

 

DB레벨에서는 당연히 한 쿼리로 구현 불가.

며칠 삽질해봤으나 이건.. 안됐다. 특히 살아있는 후손이 있는지 체크하는게 대댓글 정렬 함수를 돌리면서 동시에 쿼리로 처리 안됨

 

그래서 어떻게 했냐?

1번까지는 쿼리에서 조인을 통해 제외할 수 있었으나

2.22번 후손체크같은 경우는 

 

.

.

.

[JAVA] 반복문에서 배열요소 삭제 에서 to be continued..

 

 

[JAVA] 반복문에서 배열요소 삭제

무한댓글 구현 + 삭제여부 확인하며 새로 알게된 개념 메모 참고) https://idleday.tistory.com/109 계층형 쿼리 공지사항 게시판에 댓글 기능을 도입한다고 한다. 댓글-대댓글로 2 depth인 줄 알았으나, 대

idleday.tistory.com

 

 

 


Ref1. 머리싸매며 알게된 재밋는 자료들..


Ref2. 계층형쿼리 함수