http://www.okjsp.pe.kr/seq/98829

계층형(답변형) 게시판에서 최근 답변이 하단으로 배치되길 원해서 방법을 찾던 중
ASP로 설명된 소스가 있어 그것을 기반으로 정리 해봤습니다.
http://www.dotnetkorea.com/boardview.asp?BoardName=ServerScript&Num=186

최근 답변이 상단으로 가는것은 부모글의 STEP보다 큰 글들을 1칸씩 밀고(STEP+1)
현재 답변이 부모글 다음 STEP으로(부모STEP+1) 들어가기 때문에 위로 배치됩니다.

select * from BOARD
order by REF desc, STEP asc

NO REF STEP LVL SBJ
2 2 0 0 2
3 2 1 1 _2-1
1 1 0 0 1

위 상태에서 2번글에 답변을 달면

update BOARD
   set STEP = STEP + 1
 where REF = 2    --부모REF
   and STEP > 0   --부모STEP

ref = 2 + 1   // 부모REF + 1
lvl = 0 + 1   // 부모STEP + 1

이런 방식으로 처리가 되므로 결과는 아래와 같이 상단으로 배치됩니다.

NO REF STEP LVL SBJ
2 2 0 0 2
4 2 1 1 _2-2
3 2 2 1 _2-1
1 1 0 0 1


이제 답변을 하단로 배치시키는 방법을 설명하겠습니다.
아래 상태가 원하는 형태이고 이 상태에서 설명합니다.

NO REF STEP LVL SBJ
2 2 0 0 2
3 2 1 1 _2-1
5 2 2 2 __2-1-1
7 2 3 3 ___2-1-1-1
4 2 4 1 _2-2
6 2 5 2 __2-2-1
1 1 0 0 1

위 상태에서 답변을 할때 배치될 위치를 찾아보면 두가지 형태로 구분됩니다.
2,4,6번글에 답변을 하면 6,1번글 사이에 배치되면 됩니다.
3,5,7번글에 답변을 하면 7,4번글 사이에 배치되면 됩니다.

조금 바꿔 말하면
2,4,6번글에 답변을 하면 마지막 STEP 다음으로 들어가면 됩니다.
3,5,7번글에 답변을 하면 4번글의 STEP으로 끼어 들어가면 됩니다.

코딩하는 식으로 설명하면
2,4,6번글에 답변을 하면 최대STEP에 +1한 후 현재STEP으로 세팅해서 등록하면 됩니다.
3,5,7번글에 답변을 하면 4번글의 STEP보다 크거나 같은 글들의 STEP을 +1하고
현재STEP을 4번글의 STEP으로 세팅해서 등록하면 됩니다.

이제 해결할게 2,4,6번글과 3,5,7번글을 구분하는 방법과 4번글 STEP을 찾을 수 있으면 되는데
아래 SQL으로 구분하고 찾을 수 있습니다.

select min(STEP) from BOARD
 where REF = 부모REF
   and STEP > 부모STEP
   and LVL <= 부모LVL

이 쿼리 결과가 null이면 2,4,6번글 형태(맨 밑으로 가는)이고
null이 아니면 3,5,7번글 형태(중간에 끼어 들어가는)이며 결과가 4번글의 STEP이 됩니다.

step = 결과STEP;

if (step == null) then {  // 맨 밑으로 가는 형태
    select max(STEP) + 1
      from BOARD
     where REF = 부모REF
  
    step = 결과STEP;
} else {                  // 중간에 끼어 들어가는 형태
    update BOARD
       set STEP = STEP + 1
     where REF = 부모REF
       and STEP >= step
}

lvl = 부모LVL + 1;

이렇게 처리 후 insert하면 하단으로 답변이 배치됩니다.


NO REF STEP LVL SBJ
2 2 0 0 2
3 2 1 1 _2-1
5 2 2 2 __2-1-1
7 2 3 3 ___2-1-1-1
4 2 4 1 _2-2
6 2 5 2 __2-2-1
1 1 0 0 1

4번글에 답변을 하면
NO REF STEP LVL SBJ
2 2 0 0 2
3 2 1 1 _2-1
5 2 2 2 __2-1-1
7 2 3 3 ___2-1-1-1
4 2 4 1 _2-2
6 2 5 2 __2-2-1
8 2 6 2 __2-2-2
1 1 0 0 1

3번글에 답변을 하면
NO REF STEP LVL SBJ
2 2 0 0 2
3 2 1 1 _2-1
5 2 2 2 __2-1-1
7 2 3 3 ___2-1-1-1
8 2 4 2 __2-1-2
4 2 5 1 _2-2
6 2 6 2 __2-2-1
1 1 0 0 1


삭제시 자식글이 있는지 체크나 자식글 모두 삭제에도 처음 SQL을 이용하면 됩니다.

NO REF STEP LVL SBJ
2 2 0 0 2
3 2 1 1 _2-1
5 2 2 2 __2-1-1
7 2 3 3 ___2-1-1-1
4 2 4 1 _2-2
6 2 5 2 __2-2-1
1 1 0 0 1

4번글의 자식글 목록
select min(STEP) from BOARD
 where REF = 2    --4번REF
   and STEP > 4   --4번STEP
   and LVL <= 1   --4번LVL

결과 없음 : null

select * from BOARD
 where REF = 2    --3번REF
   and STEP > 4   --3번STEP
order by REF desc, STEP asc

결과 : 6번글


3번글의 자식글 목록
select min(STEP) from BOARD
 where REF = 2    --3번REF
   and STEP > 1   --3번STEP
   and LVL <= 1   --3번LVL

결과 있음 : 4

select * from BOARD
 where REF = 2    --3번REF
   and STEP > 1   --3번STEP
   and STEP < 4   --결과STEP
order by REF desc, STEP asc

결과 : 5,7번글


한가지 더 얘기하면 게시글목록에서 order by REF desc, STEP asc로 정렬을 하는데
REF, SET에 대한 인덱스를 잡고 신규글 등록할때 REF를 음수로 등록하면
order by REF asc, STEP asc가 가능하므로 쉽게 index scan을 할 수 있습니다.

기존방식
select * from BOARD
order by REF desc, STEP asc

NO REF STEP LVL SBJ
2 2 0 0 2
3 2 1 1 _2-1
5 2 2 2 __2-1-1
7 2 3 3 ___2-1-1-1
4 2 4 1 _2-2
6 2 5 2 __2-2-1
1 1 0 0 1

SELECT STATEMENT Optimizer Mode=CHOOSE
  SORT ORDER BY
    TABLE ACCESS FULL  BOARD

음수방식
select * from BOARD
order by REF, STEP

NO REF STEP LVL SBJ
2 -2 0 0 2
3 -2 1 1 _2-1
5 -2 2 2 __2-1-1
7 -2 3 3 ___2-1-1-1
4 -2 4 1 _2-2
6 -2 5 2 __2-2-1
1 -1 0 0 1

SELECT STATEMENT Optimizer Mode=CHOOSE
  TABLE ACCESS BY INDEX ROWID BOARD
    INDEX FULL SCAN  IX_BOARD


아래는 테스트한 테이블과 샘플데이터에 대한 SQL입니다.

CREATE TABLE BOARD
(
  NO    INT             NOT NULL,
  REF   INT             NOT NULL,
  STEP  INT             DEFAULT 0   NOT NULL,
  LVL   INT             DEFAULT 0   NOT NULL,
  SBJ   VARCHAR(100),
  PRIMARY KEY (NO)
);

CREATE INDEX IX_BOARD ON BOARD (REF, STEP);

INSERT INTO BOARD (NO, REF, STEP, LVL, SBJ) VALUES (1, 1, 0, 0, '1');
INSERT INTO BOARD (NO, REF, STEP, LVL, SBJ) VALUES (2, 2, 0, 0, '2');
INSERT INTO BOARD (NO, REF, STEP, LVL, SBJ) VALUES (3, 2, 1, 1, '_2-1');
INSERT INTO BOARD (NO, REF, STEP, LVL, SBJ) VALUES (4, 2, 4, 1, '_2-2');
INSERT INTO BOARD (NO, REF, STEP, LVL, SBJ) VALUES (5, 2, 2, 2, '__2-1-1');
INSERT INTO BOARD (NO, REF, STEP, LVL, SBJ) VALUES (6, 2, 5, 2, '__2-2-1');
INSERT INTO BOARD (NO, REF, STEP, LVL, SBJ) VALUES (7, 2, 3, 3, '___2-1-1-1');
COMMIT;

이제 javaservice.net처럼 최근 답변이 아래로 가는 게시판을 만들어 보세요.

 


번호 제목 글쓴이 날짜 조회 수
347 java.util.Optional 에서의 map() 함수의 이해 황제낙엽 2026.06.24 666
346 linux(ubuntu) 의 jdk 21 이 설정된 환경에서 jdk 17 로 다운그레이드 절차 황제낙엽 2025.09.22 1532
345 [ChatGPT] javax.* 패키지와 jakarta.* 패키지의 혼합 사용 방안 황제낙엽 2025.04.15 2459
344 [ChatGPT] *.servlet.http.HttpServlet 클래스를 사용할 수 있는 주요 WAS 목록 황제낙엽 2025.04.14 2038
343 [ChatGPT] JDBC API에서 java.sql과 javax.sql 패키지의 차이점 황제낙엽 2025.01.31 2218
342 jakarta.servlet 패키지를 이용한 File Upload 구현 (2024.03.03, Servlet 5.0) 황제낙엽 2025.01.02 2310
341 [ChatGPT] Java EE가 Jakarta EE로 전환 황제낙엽 2025.01.01 2525
340 [ChatGPT] File.delete() 함수로 파일 삭제가 되지 않는 경우 황제낙엽 2024.12.10 2809
339 [ChatGPT] <jsp:include>에서 flush="true"를 설정시의 몇 가지 문제점 과 대안 황제낙엽 2024.07.19 2493
338 [Copilot] JSP, Servlet, web.xml, mysql 한글 설정 황제낙엽 2024.07.12 2555
337 Base64 클래스의 encode, decode 에서 URL-safe 황제낙엽 2024.07.10 2624
336 [Copilot] JSP 에서 다른 페이지를 현재 페이지에 포함시키는 문법 (include) 황제낙엽 2024.06.26 2570
335 Exception 출력에 대한 고찰 황제낙엽 2024.06.10 2198
334 google-auth-library-oauth2-http 라이브러리 다운로드 황제낙엽 2023.11.19 4025
333 firebase-admin-java 라이브러리 다운로드 (firebase admin sdk library) 황제낙엽 2023.11.19 2805
332 Enum 활용 (개인블로그, Effective Java) file 황제낙엽 2023.11.02 2759
331 Enum 활용 (우아한기술블로그) file 황제낙엽 2023.11.02 2790
330 servlet name 에 camel case 를 사용해서는 안되는 이유 황제낙엽 2023.10.24 2508
329 org.apache.commons.fileupload.servlet.ServletFileUpload 이용 예시 황제낙엽 2023.08.25 3336
328 ServletFileUpload.parseRequest() 실행 결과 리스트가 비어있는 오류 황제낙엽 2023.08.25 2935