본 게시판의 장점은

"게시물의 갯수와 상관없이 (DB용량이 받쳐주는대로) 페이징/페이지 검색 속도가 동일하다는 것"입니다.


대신 단점은

1. 오래된 게시물의 답변 글을 쓸 경우에는 timeout 이 발생할 수 있다.

2. 삭제시 레코드를 직접 삭제하는 방법보다는 "xxx에 의해 삭제되었다.."라는 형태를 쓰는 것을 권장한다.


1번같은 경우는 먼저도 말했지만

게시판이라는것이 주로 테이블의 윗부분(최근글)에서 빈번한 삽입과 답변이 이루어지는 것을 감안하였을 때

심각하게 고려할 만한 사항은 아니라고 봅니다. 아무리 중간에 삽입하는 것이 느리더라도

테스트해보시면 아시겠지만, 100페이지 정도에서 답변글을 달아도 느리다는 느낌을 전혀 주지 않습니다.


그럼... 설명에 앞서 준비를 하겠습니다.

두 개의 소스를 준비하였습니다. QA에서 실행시키면 될 것입니다.


*************************************************************************

1. 테이블 (편의상 [t3]이라는 테이블로 작성하였습니다.

*************************************************************************

/****** Object: Table [dbo].[t3] Script Date: 2003-07-19 오전 12:37:23 ******/

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[t3]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)

drop table [dbo].[t3]

GO


/****** Object: Table [dbo].[t3] Script Date: 2003-07-19 오전 12:37:23 ******/

CREATE TABLE [dbo].[t3] (

[seq] [int] IDENTITY (1, 1) NOT NULL ,

[ref] [int] NOT NULL ,

[depth] [smallint] NOT NULL ,

[name] [varchar] (20) COLLATE Korean_Wansung_CI_AS NOT NULL ,

[email] [varchar] (50) COLLATE Korean_Wansung_CI_AS NULL ,

[password] [varchar] (16) COLLATE Korean_Wansung_CI_AS NOT NULL ,

[title] [varchar] (100) COLLATE Korean_Wansung_CI_AS NOT NULL ,

[writeday] [datetime] NOT NULL ,

[readnum] [smallint] NOT NULL ,

[ip] [varchar] (15) COLLATE Korean_Wansung_CI_AS NOT NULL ,

[content] [text] COLLATE Korean_Wansung_CI_AS NOT NULL

) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO


ALTER TABLE [dbo].[t3] WITH NOCHECK ADD

CONSTRAINT [PK_t3] PRIMARY KEY CLUSTERED

(

[seq] DESC

) ON [PRIMARY]

GO


ALTER TABLE [dbo].[t3] ADD

CONSTRAINT [DF_t3_depth] DEFAULT (0) FOR [depth],

CONSTRAINT [DF_t3_writeday] DEFAULT (getdate()) FOR [writeday],

CONSTRAINT [DF_t3_readcount] DEFAULT (0) FOR [readnum]

GO


CREATE INDEX [IX_t3] ON [dbo].[t3]([ref] DESC ) ON [PRIMARY]

GO



*************************************************************************

2. 데이터 입력

*************************************************************************


set nocount on

--변수 선언

declare @name varchar(20)

declare @email varchar(50)

declare @password varchar(20)

declare @title varchar(100)

declare @ip varchar(15)

declare @content varchar(200)

--루프용변수

declare @iCount int

--초기화

set @name = '이성근'

set @email = 'rainn@rainn.pe.kr'

set @password = 'abcdefg'

set @ip = '127.0.0.1'


select @iCount = max(seq) from t3


set @iCount = @iCount + 1

while (@iCount <= 1000000) -- 이 조건문에 1,000,000 을 주면 100만개의 레코드를 만듭니다.

begin

set @title = cast(@iCount as varchar) + ' 번째 글입니다.'

set @content = cast(@iCount as varchar) + ' 번째 글의 내용입니다.'


insert into t3 (ref, name, email, password, title, ip, content) values (@iCount, @name, @email,@password, @title, @ip, @content)

set @iCount = @iCount + 1

end




이하 설명은 다음 강좌에서 하겠습니다. ^^*

집어치우라고 돌던지셔도 좋습니다 ^^;;


지난 시간에는 테이블 생성 스크립트와 자동 게시물 삽입 스크립트를 살펴 보았습니다. 이번 시간에는 게시판 테이블에 레코드가 삽입되는 로직에 대해서 알아보겠습니다.
/****** Object: Table [dbo].[t3] Script Date: 2003-07-19 오전 12:37:23 ******/ CREATE TABLE [dbo].[t3] ( [seq] [int] IDENTITY (1, 1) NOT NULL , [ref] [int] NOT NULL , [depth] [smallint] NOT NULL , [name] [varchar] (20) COLLATE Korean_Wansung_CI_AS NOT NULL , [email] [varchar] (50) COLLATE Korean_Wansung_CI_AS NULL , [password] [varchar] (16) COLLATE Korean_Wansung_CI_AS NOT NULL , [title] [varchar] (100) COLLATE Korean_Wansung_CI_AS NOT NULL , [writeday] [datetime] NOT NULL , [readnum] [smallint] NOT NULL , [ip] [varchar] (15) COLLATE Korean_Wansung_CI_AS NOT NULL , [content] [text] COLLATE Korean_Wansung_CI_AS NOT NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ALTER TABLE [dbo].[t3] WITH NOCHECK ADD CONSTRAINT [PK_t3] PRIMARY KEY CLUSTERED ( [seq] DESC ) ON [PRIMARY] GO ALTER TABLE [dbo].[t3] ADD CONSTRAINT [DF_t3_depth] DEFAULT (0) FOR [depth], CONSTRAINT [DF_t3_writeday] DEFAULT (getdate()) FOR [writeday], CONSTRAINT [DF_t3_readcount] DEFAULT (0) FOR [readnum] GO CREATE INDEX [IX_t3] ON [dbo].[t3]([ref] DESC ) ON [PRIMARY] GO 간략한 테이블 명세표 입니다. seq : 글번호 ref : 글순서 depth : 답글깊이 name : 이름 email : email password : 암호 title : 제목 writeday : 글쓴시각 readnum : 조회수 ip : 글쓴이의 IP content : 내용 특징은
seq : PK (DESC) ref : Index (DESC) 라는 점 이외엔 없지요. ref 라는 컬럼은 글순서 입니다. 여러가지 ref 컬럼의 설계 로직들이 있었으나,
본 게시판에서 ref는 단순 정수형의 컬럼이면서 글 순서가 들어갑니다. 즉 시나리오는 다음과 같습니다. ----------------------------------------------- 1. 사용자가 글을 입력

seqrefdepth
110

2. 이후로 10개 입력

seqrefdepth
10100
990
880
770
660
550
440
330
220
110

3. 4번글의 답변글 입력

seqrefdepth
10110
9100
890
780
670
560
450
1141
330
220
110

빨간색 부분을 유심히 보아 주시기 바랍니다.
답변글이 입력 될때는 부모글(seq:4, ref:4)이 있지요.
일단 부모글의 ref(4) 가 지금 들어갈 글(11번) 의 ref(=4)로 결정하고,
부모글의 ref(4였겠죠) 이상인 ref를 모조리 +1 update 합니다.
(query : update [테이블] set ref = ref + 1 where ref >= [부모글의 ref] )
depth는 항상 부모글의 depth + 1 해주면 간단합니다.

오래된 게시물일 경우 시간이 오래 걸리는 이유는 바로 이 때문입니다.
하지만 무시할만큼의 이유는 충분하다고 생각합니다. 게시판의 특성상...

이해를 돕기 위해 세가지 경우를 더 보여드립니다.

4. 11번글(4번글의 첫번째 답변글)의 답변글 입력

먼저 추측을 하면,
11번글(seq:11, ref:4) 이 부모글이니까
이번글의 ref는 4가 되겠죠. 그래서 일단 ref가 4 이상인 ref를 +1 시켜주고
자신의 ref를 4로서 insert 합니다.
(순서에 주의하셔야 합니다! update가 먼저고, insert가 나중이어야 합니다.)

seqrefdepth
10120
9110
8100
790
680
570
460
1151
1242
330
220
110

5. 새글 입력

seqrefdepth
13130
10120
9110
8100
790
680
570
460
1151
1242
330
220
110
6. 13번글의 답변글 입력

seqrefdepth
13140
14131
10120
9110
8100
790
680
570
460
1151
1242
330
220
110


이해가 되십니까?

가장 상위의 ref 값을 게시물의 개수로 활용할 수도 있습니다.
MAX 함수를 쓰실 필요 없이 "SELECT TOP 1 ref FROM t3" 하시면 됩니다. index 가 걸려 있기 때문에..


이해가 빠르신 분들은 여기까지만 들어도 구현하실 수 있으리라 봅니다.
다음시간에는 asp페이지로 구현하는 부분에 대해서 설명하도록 하겠습니다.
감사합니다.


===============================
이성근 (rainn@rainn.pe.kr)
Soongsil Univ. Computer Science '95
(주)산그림닷컴
===============================
------------------------------------------------------------------

지난 시간에는 게시판 테이블에 레코드가 삽입되는 로직에 대해서 알아 보았습니다.
이번 시간에는 list 페이지에 필요한 asp 코드들을 대략 살펴보겠습니다.

---------------------------------------------------------------------

지난 강좌를 보셔서 아시겠지만 현재 테이블은 ref의 역순이 그 글이 보여지는 순서가 됩니다.

페이징은 간단하게 처리할 수 있겠습니다.

우리가 원하는 글의 ref 만을 불러오면 되는 거죠!

(사실 이 방법을 쓰기 전까지
select top * ... 과 rs.move 를 이용한 게시판이 그나마 젤 나은 성능을 보였습니다.)

원하는 글의 ref 불러오는 방법은..

SELECT [컬럼1], [컬럼2], .... FROM [테이블] ref BETWEEN [가장아래에보여줄글번호의ref] AND [가장위에보여줄글번호의ref]
ORDER BY ref DESC

가 되겠습니다.
간단하지 않습니까?
"ORDER BY ref DESC"에 의한 속도 저하는 없다고 보시면 됩니다.
제가 알기로는 ref에 걸린 index로 인해 쿼리최적화기기가 무시하게 되는듯 합니다.
(즉 ORDER BY 이하를 생략하더라도 글 순서는 변하지 않습니다. index를 DESC로 만들어준 이유가 거기에 있는거죠..)

이 방법으로 인해 우리는 페이지 값에 상관없이 언제나 동일한 페이징 속도를 낼 수가 있게 되는 거심니다!

그럼 우리가 해야 할 일은
시작ref와 끝ref 만 구해주면 되겠습니다.

자, 답은 다음과 같습니다.

start_num = record_count - (page - 1) * record_size
end_num = start_num - record_size + 1

start_num : 시작번호
end_num : 끝번호
record_count : 레코드 수이면서 가장 최상위글의 ref 입니다.
record_size : 한 페이지에 보여줄 게시물의 수
page : 현재 페이지 번호

record_count 는 지난번에도 말씀드렸듯이

SELECT TOP 1 ref FROM t3

으로 구하시면 되겠지만, 중간에 레코드를 삭제하여 이빨이 빠져있으면 안됩니다.
그러면 최상위 ref = 레코드 수 라는 법칙이 깨져버리기 때문입니다.

굳이 레코드를 삭제하고 싶으시면 ref를 적절히 변경해 주셔야 하겠죠?
- 지우고자 하는 글의 ref를 기억한다음
- 일단 레코드를 지우고
- 지운 글의 ref 보다 큰 ref를 가지는 레코드들의 ref를 1씩 빼주시면 됩니다.

하지만 답변형 게시판에서의 삭제 로직은 레코드 삭제를 대부분 비추하는 형태이죠..
답변글의 부모글이 지워지는 경우 화면 구성이 이상하게 될 수도 있겠죠..


record_size 는 원하는 값으로 아무 값이나 주시면 되겠죠..

시작ref와 끝ref 를 다 구했으면
위에 설명한대로 원하는 레코드만 샤악~ 불러오면 됩니다!

sql = "SELECT seq, title, name, writeday, readnum, depth FROM t3 WHERE ref BETWEEN " & end_num & " AND " & start_num & " ORDER BY ref DESC"
rs.Open sql, dbcon, adOpenForwardOnly, adLockReadOnly, adCmdText

보다 나은 속도를 위해 CURSORTYPE, LOCKTYPE, OPTION을 명시해 주시면 되겠습니다.
주의할 점은 start_num 과 end_num 의 위치입니다.!! 꼭 순서에 주의해 주시기 바랍니다.
순서 바꾸고 제대로 안나온다고 저한테 돌던지지 마세요 ^^*

참고로 총 페이지 수(page_count)는

page_count = int(record_count / record_size)
if (page_count * record_size) <> record_count then page_count = page_count + 1

와 같이 구하시면 되겠습니다.




다음시간에는 write, reply 에 대해서 알아보겠습니다.
감사합니다.

지난 시간에는 list 페이지에 필요한 asp 코드들을 대략 살펴보았습니다.
이번 시간에는 write, reply 페이지에 필요한 asp 코드들을 살펴보겠습니다.

---------------------------------------------------------------------

이미 이전의 강좌들로 충분히 아실 수 있을거라 생각합니다.
간단하게 정리하겠습니다.

1. 새글 쓰기
- 새글의ref = 최상위ref + 1

코드 예제)

sql = "select top 1 ref from t3"
rs.open sql, dbcon, adOpenForwardOnly, adLockReadOnly, adCmdText

if (rs.bof or rs.eof) then
ref = 1
else
ref = rs(0) + 1
end if
rs.close

sql = "insert into t3 (ref, name, email, password, title, ip, content) values ("
... 이하 생략...


2. 답변글 쓰기
- 답변글의ref = 부모글의ref
- ref가 부모글의 ref 이상인 레코드들의 ref를 각각 +1 증가

코드 예제)

ref = view_ref ' 답변글의 ref = 부모글의 ref
depth = view_depth + 1 ' 답변글의 depth = 부모글의 depth + 1

sql = "update t3 set ref = ref + 1 where ref >= " & ref
dbcon.execute sql,, adCmdText + adExecuteNoRecords

sql = "insert into " & table_name & " (ref, depth, name, email, password, title, ip, content) values ("
... 이하 생략...



이상으로 허접한 강좌가 마무리 되겠습니다.
자, 여기서 욕심을 내셔서 stored procedure를 사용하는 게시판으로 변형하시는 것은
여러분들의 몫이고 또한 실험정신이겠죠! ^^

그동안 읽어주셔서 감사합니다.

강좌쓰기 힘들군요. ^^;;; 글솜씨가 없다보니..

기타 하시고 싶은 말씀이나, 질문은 이곳 데브피아 게시판 혹은 위 URL로 접속하셔서 남겨주세요 ^^


아... 그리고 소스를 공개해달라는 부탁을 받았는데요,,,
소스 공개는 하지 않을 생각입니다.
핵심 부분은 이미 다 말했고, 나머지는 여러분의 힘으로 가능하시리라 보니까요. ^^
왠지 고기를 잡아주기 보다는 고기낚는 법을 알려주고 싶어하고픈 욕심이라 보시면 되겠습니다. ^^;;;;; 에공..
===============================
이성근 (rainn@rainn.pe.kr)
Soongsil Univ. Computer Science '95
(주)산그림닷컴
===============================


'DataBase' 카테고리의 다른 글

DB지식 테스트  (0) 2007.09.04
김종균 : SQL Server 2005 .NET CLR 통합기능  (0) 2007.08.25
MS-SQL 페이징 처리  (0) 2007.08.19
SQL 25가지 기법  (0) 2007.08.19
컬럼이랑 테이블 찾기..  (0) 2007.07.23
Posted by 퓨전마법사
,