﻿/*******************************************************************************
  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
