/******************************************************************************* MSSQL 학습 트랙 — 5부(성능)용 대량 데이터 5부의 예제는 50만 행짜리 표를 사용합니다. 실습 데이터베이스의 507행으로는 무엇을 해도 빠르게 끝나 계획의 차이가 숫자로 드러나지 않기 때문입니다. * lab-setup.sql 로 MssqlLab 을 만든 뒤에 실행하십시오. * Board.POSTS 는 건드리지 않습니다. 같은 모양의 Board.POSTS_BIG 을 따로 만듭니다. 1~4부의 예제 결과가 그대로 유지됩니다. * 난수와 현재 시각을 사용하지 않습니다. 누가 언제 돌려도 같은 자료가 만들어져야 단원에 적어 둔 결과와 화면이 맞아떨어집니다. * 5부를 마치면 아래 한 줄로 지웁니다. 100MB 가까이 차지합니다. DROP TABLE Board.POSTS_BIG; 만들어지는 것 표 1개 Board.POSTS_BIG 50만 행 (약 57MB) 인덱스 2개 IX_POSTS_BIG_list 목록용 (약 35MB) IX_POSTS_BIG_user 회원별 조회용 (약 6MB) 만드는 데 걸리는 시간은 장비에 따라 다르지만 대개 몇 초입니다. ********************************************************************************/ SET NOCOUNT ON; GO USE MssqlLab; GO IF OBJECT_ID('Board.POSTS_BIG') IS NOT NULL DROP TABLE Board.POSTS_BIG; GO /*------------------------------------------------------------------------------ 1. 표 Board.POSTS 와 같은 모양입니다. 다만 계층 열(parent_num · group_num · depth · sort_no)은 자리만 두고 모두 원글로 채웁니다. 5부는 계층이 아니라 규모를 보는 단원이라 답글을 만들 까닭이 없습니다. ------------------------------------------------------------------------------*/ CREATE TABLE Board.POSTS_BIG ( num INT IDENTITY(1, 1) NOT NULL, board_num INT NOT NULL, category_num INT NULL, user_num INT NOT NULL, parent_num INT NULL, group_num INT NOT NULL, depth SMALLINT NOT NULL, sort_no INT NOT NULL, title NVARCHAR(200) NOT NULL, content NVARCHAR(MAX) NULL, hit_count INT NOT NULL, reg_date DATETIME2(0) NOT NULL, mod_date DATETIME2(0) NULL, CONSTRAINT PK_POSTS_BIG PRIMARY KEY CLUSTERED (num) ); GO /*------------------------------------------------------------------------------ 2. 자료 50만 행 숫자 표를 만들어 한 문장으로 넣습니다(4.8). 한 행씩 넣으면 몇 분이 걸립니다. 값은 모두 행 번호 i 의 나머지 연산으로 만들어 언제 돌려도 같습니다. board_num i % 3 → 게시판마다 약 16만 6천 건 user_num i % 20 → 회원마다 2만 5천 건 title i % 8 → 여덟 낱말이 6만 2천 5백 건씩 hit_count i % 1000 → 0~999 가 고르게 reg_date 1분씩 증가 → 2026-01-01 부터 약 347일 ------------------------------------------------------------------------------*/ DECLARE @start datetime2(7) = SYSDATETIME(); WITH N1 AS (SELECT 1 AS n FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) v(x)), N2 AS (SELECT 1 AS n FROM N1 a CROSS JOIN N1 b), N3 AS (SELECT 1 AS n FROM N2 a CROSS JOIN N2 b), N4 AS (SELECT TOP (500000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS i FROM N3 a CROSS JOIN N3 b) INSERT INTO Board.POSTS_BIG (board_num, category_num, user_num, parent_num, group_num, depth, sort_no, title, content, hit_count, reg_date) SELECT (i % 3) + 1, CASE WHEN (i % 3) + 1 = 1 THEN NULL ELSE ((i % 10) + 1) END, -- 공지는 말머리를 쓰지 않습니다 (i % 20) + 1, NULL, i, 0, 0, N'글 제목 ' + CAST(i AS NVARCHAR(10)) + N' ' + CASE i % 8 WHEN 0 THEN N'인덱스' WHEN 1 THEN N'조인' WHEN 2 THEN N'트랜잭션' WHEN 3 THEN N'정규화' WHEN 4 THEN N'커서' WHEN 5 THEN N'뷰' WHEN 6 THEN N'제약 조건' ELSE N'실행 계획' END, N'본문입니다. ' + CAST(i AS NVARCHAR(10)), i % 1000, DATEADD(minute, i, '2026-01-01T00:00:00') FROM N4; PRINT CONCAT(N' 자료 ', @@ROWCOUNT, N' 행을 넣었습니다.'); GO /*------------------------------------------------------------------------------ 3. 인덱스 Board.POSTS 에 걸어 둔 것과 같은 모양입니다(3.5). 목록 화면이 사용하는 인덱스와, 회원별 조회에 사용하는 인덱스 둘입니다. ------------------------------------------------------------------------------*/ CREATE NONCLUSTERED INDEX IX_POSTS_BIG_list ON Board.POSTS_BIG (board_num, group_num DESC, sort_no) INCLUDE (title, user_num, reg_date, hit_count, depth); GO CREATE NONCLUSTERED INDEX IX_POSTS_BIG_user ON Board.POSTS_BIG (user_num); GO /*------------------------------------------------------------------------------ 4. 확인 ------------------------------------------------------------------------------*/ SELECT (SELECT COUNT(*) FROM Board.POSTS_BIG) AS 행수, (SELECT COUNT(*) FROM Board.POSTS_BIG WHERE board_num = 1) AS 공지, (SELECT COUNT(*) FROM Board.POSTS_BIG WHERE user_num = 7) AS 회원7의글, (SELECT COUNT(*) FROM Board.POSTS_BIG WHERE title LIKE N'%인덱스%') AS 인덱스제목; SELECT i.name AS 인덱스, ps.page_count AS 페이지, ps.page_count * 8 / 1024 AS MB FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('Board.POSTS_BIG'), NULL, NULL, 'DETAILED') ps JOIN sys.indexes i ON i.object_id = ps.object_id AND i.index_id = ps.index_id WHERE ps.index_level = 0 ORDER BY i.index_id; GO PRINT N''; PRINT N' 준비가 끝났습니다. 5부를 마치면 DROP TABLE Board.POSTS_BIG; 으로 지우십시오.'; GO