Chapter 2. MS SQL을 이용한 데이터베이스 응용

개발하는 운동인·2025년 9월 24일

4주차 - 데이터 베이스의 이해

정보 저장

  • 데이터의 연속성을 위해서 정보 저장이 필요함

서버에 저장

  • 로컬에서 어떤 파일을 저장했을 때, 다른 컴퓨터에서 저장한 파일을 불러올 수 없기 때문에, 데이터베이스를 이용해서 언제 어디서든지 데이터를 불러올 수 있다. ->SaveFile
  • SQLServer 프로그램이 있다. 이 프로그램은 서버에서 데이터를 받고 저장하기 때문에, 서버의 기능이 갖춰져 있어야 한다.

파일

  • RDB로 할 수 없을 때는 파일로 저장하면 된다. -> 데이터 베이스의 기본

파일과 데이터 베이스 비교

파일 > DBMS : 저장 및 로딩 속도

  • 속도는 파일 저장이 더 빠르지만 DBMS는 데이터 관리와 분석하기 위해서 사용한다. 데이터가 많고, 용량이 많아지거나, 하드 관리가 힘들어질 때가 있는데 그 때는 파일 저장보다는 DBMS로 관리하는 것이 좋다.
  • DBMS도 결국 파일 시스템을 사용한다.

파일 X 데이터 베이스 O - 데이터 백업 및 복원 기능

  • 백업 서버에 데이터를 날리면 바로 저장하고,데이터베이스의 로그파일을 남겨서 복원할 수도 있다. => 백업본은 항상 남겨둬야 한다. 없으면 서비스를 닫아야 할 수도

데이터베이스의 사용

MS SQL Server - RDB 많이 사용 많이 사용

  • Oracle MySQL - RDB 많이 사용

데이터 베이스의 구성

  • 데이터 베이스
    GameDB라는 인스턴스
    • 테이블 생성
      • 행 ( 레코드 ) 0
      • 열 (필드) 이름,변수 타입
  • 데이터베이스 열의 타입
    정수
    소수
    문자열
    날짜, 시간
    그 외(바이너리)
  • LogDB에 중요한 것은 날짜

MS SQL Server 실습

  • NULL 허용 선택 시, 값을 정해야 한다. 정하지 않으면 DB입장에서 NULL를 반환하여 오류가 뜬다.

  • 테이블 클릭

  • 데이터 베이스 우클릭 하여 새 데이터베이스를 만들어본다.

  • 이름은 Test

  • ID,PW 입력
  • 컨 + s 눌러 저장
  • 새 쿼리 선택

새 테이블 만들기 - 쿼리문 이용

  • 아래처럼 작성 후 f5
  • 새로고침 누르면 생기는 것을 볼 수 있다.

필드 유형

varchar(n) : 유동성 있는 문자열. 최대 길이 n의 문자열
nvarchar,nchar : n이 붙으면 유니코드
text : 길이 무제한 문자열 : 읽기/쓰기 속도가 느리다.
binary : 길이 무제한 바이너리

SQUL 질의 구문

  • 데이터 베이스를 액세스 할 때 필수적으로 알아야 하는 문법
  • 실무에서 신입들에게 운영 툴이나 UI 업무를 많이 주는데, 웹 서비스를 만들때 유용한 API는 아래와 같다
    - CRUD
    - CREATE - 추가하기
    - READ - 읽기
    - 변경
    - 삭제
  • 추가하기 ( insert)
    - insert into KJ (a,b,c) into (1,2,3) (설명 : KJ 테이블에 a,b,c에 1,2,3을 넣겠다)
  • 질의하기 (Select)
    - select a,b,c from KJ where condition (설명 : KJ테이블에서 a,b,c 필드 중에 condition인 것을 조회
  • 갱신하기 ( Update )
    - Update KJ set column = value where condition;

insert

인덱스와 키

PRIMARY KEY

  • 고유키 : 한 테이블에 하나만 추가, 중복 허락 X , 필드 값은 NULL 허용 X

고유키를 지정하면서 테이블 생성

플레이어 데이터를 DB에 저장하는 방법

  • 플레이어 정보는 다양한 형태의 데이터로 구성
  • 최상단
    - ID,암호화된 비밀번호, 실제 이름
    • 하나 이상의 캐릭터
      • 이름,종족,기타 능력치
      • 아이템
        - 아이템의 종류, 수량 속성

플레이어 데이터를 구성하는 각각의 노드를 테이블에 넣는ㄴ 방법

  • 부모 노드의 pk와 자식 노드의 외래키와 같아야 한다. -> 관계형 db
  • 만약 pk인 id가 동양이라는를 조회하면 감정 자식 노드, 무기 자식 노드의 각각의 id가 동양인 데이터를 불러온다.

Json

  • 플레이어 데이터 저장 방법
    - 플레이어 데이

UML 표기법

질의 구문 실행(SQL)

  • Select ID, password form UserAccount where ID = '1234' (유저 정보 가져 오려고 할 때)

⭐ 중요 예제

  • 이 SQL 스키마는 온라인 게임 계정/캐릭터/아이템 관리 구조를 정의한 것이라고 보면 돼요.
CREATE TABLE dbo.UserAccount
(
    ID          INT IDENTITY(1,1) PRIMARY KEY,
    Email       NVARCHAR(255) NOT NULL,
    [Password]  NVARCHAR(255) NOT NULL,
    CreatedAt   DATETIME2(0) NOT NULL 
                    CONSTRAINT DF_UserAccount_CreatedAt DEFAULT (SYSUTCDATETIME())
);

CREATE TABLE dbo.[Character]
(
    ID                  INT IDENTITY(1,1) PRIMARY KEY,
    OwnerUserAccountID  INT NOT NULL,
    Gender              NVARCHAR(10) NULL,
    [Level]             INT NOT NULL 
                            CONSTRAINT DF_Character_Level DEFAULT(1),
    CreatedAt           DATETIME2(0) NOT NULL 
                            CONSTRAINT DF_Character_CreatedAt DEFAULT (SYSUTCDATETIME()),
    CONSTRAINT FK_Character_UserAccount
        FOREIGN KEY (OwnerUserAccountID)
        REFERENCES dbo.UserAccount(ID)
        ON DELETE CASCADE
        ON UPDATE NO ACTION
);

CREATE TABLE dbo.Item
(
    ID              INT IDENTITY(1,1) PRIMARY KEY,
    OwnerCharacterID INT NOT NULL,
    [Type]          NVARCHAR(10) NULL,
    Amount          INT NOT NULL 
                        CONSTRAINT DF_Item_Amount DEFAULT(1),
    CONSTRAINT FK_Item_Character
        FOREIGN KEY (OwnerCharacterID)
        REFERENCES dbo.[Character](ID)
        ON DELETE CASCADE
        ON UPDATE NO ACTION
);
GO

각 테이블 설명

UserAccount 테이블

CREATE TABLE dbo.UserAccount // dbo 스키마에 UserAccount 테이블 생성
ID          INT IDENTITY(1,1) PRIMARY KEY, 
- ID는 INT // 타입
- IDENTITY(1,1) // 자동증가
- PRIMARY KEY // 기본키(중복 불가, NULL 불가)

 Email       NVARCHAR(255) NOT NULL,
 - 최대 255자 유니코드 문자열 저장
 - NOT NULL - 반드시 값이 필요
[Password]  NVARCHAR(255) NOT NULL,
- 비밀번호 컬럼(예약어라 대괄호가 필요하다)
  • Password는 sql언어에 이미 있으므로 대괄호로 컬럼을 구분하여 정의.
CreatedAt DATETIME2(0) NOT NULL
- 날짜/시간 저장,소수점 없는 초 단위
 CONSTRAINT DF_UserAccount_CreatedAt DEFAULT (SYSUTCDATETIME())

내가 몰랐던 개념

    1. IDENTITY 자동 증가가 무엇이야?
    1. CreatedAt 선언에 대해 아예 모르겠음

[Character] 테이블

ID                  INT IDENTITY(1,1) PRIMARY KEY,
- ID는 기본키이며 자동증가 된다. (1,1). 초기값 1 증가값 1
OwnerUserAccountID  INT NOT NULL,
- OwnerUserAccountID는 INT 타입이로 NULL 허용하지 않는다.
 Gender              NVARCHAR(10) NULL,
 - Gender는 유니코드 문자열 최대 10자까지 저장할 수 있고 null 허용한다.
[Level]             INT NOT NULL 
                            CONSTRAINT DF_Character_Level DEFAULT(1),
- Level은 예약어라, 대괄호로 구분
- Int타입이고, null 허용하지 않는다.
- Character 테이블에서 Level의 제약 조건은 기본값이 1이다. 캐릭터 레벨, 기본값 1.
CreatedAt           DATETIME2(0) NOT NULL 
                            CONSTRAINT DF_Character_CreatedAt DEFAULT (SYSUTCDATETIME()),
- CreatedAt는 날짜/시간을 저장하는 컬럼이고, 소수점은 없다(0). null 허용하지 않는다
- Character테이블에서 CreatedAt 컬럼 제약 조건은 값을 넣지 않으면 현재 utc 서버 시간으로 저장
CONSTRAINT FK_Character_UserAccount
        FOREIGN KEY (OwnerUserAccountID)
        REFERENCES dbo.UserAccount(ID)
        ON DELETE CASCADE
        ON UPDATE NO ACTION
- CONSTRAINT을 통해 아래 4줄을 제약 조건으로 할건데 이름을 FK_Character_UserAccount로 정함
- FOREIGN KEY (OwnerUserAccountID) - 현재 테이블의 OwnerUserAccountID 컬럼을 외래키로 설정
- REFERENCES dbo.UserAccount(ID) - 외래키(OwnerUserAccountID)는 UserAccount.ID 컬럼을 참조
- ON DELETE CASCADE - 참조중인 UserAccount의 행이 삭제되면 그 UserAcount를 참조하고 있는 Character 행도 삭제됨
- ON UPDATE NO ACTION - 부모의 기본키의 값이 변경되면 외래키의 값은 변경되지 않음. 

외래키가 기본키를 참조

ON DELETE CASCADE

  • 참조중인 UserAccount의 행이 삭제되면 그 UserAcount를 참조하고 있는 Character 행 삭제됨
  • UserAccount 테이블의 기본키인 ID가 1인 행을 삭제한다 치면, 기본키가 1인 행 삭제하고 ID = 1을 참조하고 있는 외래키의 행들을 삭제 한다.

ON UPDATE NO ACTION

  • 부모의 기본키의 값이 변경되면 외래키의 값은 변경되지 않음.
  • 값이 변경되게 하고 싶다면 ON UPDATE ACTION

참조의 개념

  • 외래키의 값을 통해 부모의 기본키의 값을 알게 되어 이 캐릭터가 어떤 UserAccount에 속해 있는지 알 수 있다.

[ITEM] 테이블

  • Charater 테이블 구조와 똑같다.

각 테이블에 데이터 삽입하기

INSERT INTO dbo.UserAccount (Email,[Password])
VALUES
    ('wls6189@naver.com','5532'),
    ('vksej1235@gmail.com','6189');

INSERT INTO dbo.[Character] (OwnerUserAccountID,Gender,[LEVEL])
VALUES
    (1,'남자',5), -- ID가 1인 김진 캐릭터1
    (1,'남자',4), -- ID가 1인 김진 캐릭터2
    (2,'여자',3); -- ID가 2인 민솔 캐릭터1

INSERT INTO dbo.Item(OwnerCharacterID,[Type],Amount)
VALUES
    (1,'Potion',10), -- ID가 1인 김진캐릭터1에게 포션 10개
    (1,'Gold',500),  -- ID가 1인 김진캐릭터2에게 포션 500개
    (2,'Sword',1);  -- ID가 2인 민솔 캐릭터1에게 포션 1개

UserAccount 테이블에 데이터 삽입 내용

INSERT INTO dbo.UserAccount (Email,[Password])
- dbo 스키마에 있는 UserAccount 테이블에 Email,[Passsword] 컬럼에 값을 넣을 것이라고 명시 
    ('wls6189@naver.com','5532'), //컬럼에 맞게 문자열 입력하고 쉼표(,)를 사용하여 구분
    ('vksej1235@gmail.com','6189'); //컬럼에 맞게 문자열 입력하고 쉼표(,)를 사용하여 구분
  • ID는 따로 데이터 삽입 하지 않았다. 그 이유는 ID 컬럼을 만들 때 PK(기본키)로 설정하였고, IDENTITY(1,1)을 명시하였기에 처음에는 1부터 시작, 새 행이 삽입될 때마다 1씩 증가하게 된다.
  • 따라서 Insert 할 때 ID 컬럼은 입력하지 않는다.
  • 아래 조회 부분을 보면 Insert 할 때마다 ID가 1씩 증가되는 것을 볼 수 이싿.


Character 테이블에 데이터 삽입 내용

INSERT INTO dbo.[Character] (OwnerUserAccountID,Gender,[LEVEL])
VALUES
    (1,'남자',5), -- ID=1인 UserAccount(김진)의 캐릭터1
    (1,'남자',4), -- ID=1인 UserAccount(김진)의 캐릭터2
    (2,'여자',3); -- ID=2인 UserAccount(민솔)의 캐릭터1
    
- OwnerUserAccountID → FK. UserAccount(ID) 값을 참조해야 함.

- Gender → 성별 (남자, 여자).

- [Level] → 캐릭터 레벨.
  • 여기서 중요한 점은 OwnerUserAccountID가 외래키로 설정하였기에 UserAccount 테이블에 ID를 참조하게 됨

  • ID는 따로 데이터 삽입 하지 않았다. 그 이유는 ID 컬럼을 만들 때 PK(기본키)로 설정하였고, IDENTITY(1,1)을 명시하였기에 처음에는 1부터 시작, 새 행이 삽입될 때마다 1씩 증가하게 된다.

  • 따라서 Insert 할 때 ID 컬럼은 입력하지 않는다.

  • 아래 조회 부분을 보면 Insert 할 때마다 ID가 1씩 증가되는 것을 볼 수 이싿.


Item 테이블에 데이터 삽입 내용

INSERT INTO dbo.Item(OwnerCharacterID,[Type],Amount)
VALUES
    (1,'Potion',10), -- ID가 1인 김진캐릭터1에게 포션 10개
    (1,'Gold',500),  -- ID가 1인 김진캐릭터2에게 포션 500개
    (2,'Sword',1);  -- ID가 2인 민솔 캐릭터1에게 포션 1개
- OwnerCharacterID → FK. Character(ID)를 참조해야 함.

- [Type] → 아이템 종류 (Potion, Gold, Sword 등).

- Amount → 아이템 수량.
  • 여기서 중요한 점은 OwnerCharacterID가 외래키로 설정하였기에 UserAccount 테이블에 ID를 참조하게 됨

  • ID는 따로 데이터 삽입 하지 않았다. 그 이유는 ID 컬럼을 만들 때 PK(기본키)로 설정하였고, IDENTITY(1,1)을 명시하였기에 처음에는 1부터 시작, 새 행이 삽입될 때마다 1씩 증가하게 된다.

  • 따라서 Insert 할 때 ID 컬럼은 입력하지 않는다.

  • 아래 조회 부분을 보면 Insert 할 때마다 ID가 1씩 증가되는 것을 볼 수 이싿.


⭐ 새 쿼리를 만들어서 문제 풀어보자.

    1. 각 테이블의 모든 정보를 보는 쿼리문
    1. 캐릭터으이 레벨이 4 이상인 캐릭터의 정보를 보는 쿼리문
    1. 아이템양이 gold인 아이템 정보를 보는 쿼리문
    1. 유저 id가 1인 유저의 아이템을 보는 쿼리문
    1. ItemID가 1인 아이템을 가지고 있는 유저를 가져오는 쿼리문
    1. 캐릭터의 gender가 Male인 캐릭터를 보유하고 있는 유저를 가져오는 쿼리문

1번

  • 처음에 아래 빨간색 박스처럼 입력했는데 Charatcter 개체가 존재하지 않다고 오류가 나타났다.그때는
USE 데이터베이스이름
GO
  • 이렇게 입력하면 존재하게 된다.

  • 아래 코드 처럼 작성하면 조회할 수 있다.

  • SELECT * FROM 스키마.테이블이름 형식으로 세 테이블 모두 입력하면 세 테이블이 조회가 된다.

2번 - 캐릭터의 레벨이 4 이상인 캐릭터의 정보를 보는 쿼리문

  • SELECT * FROM dbo.테이블명 WHERE 컬럼명 을 사용하여 if 조건문 처럼 사용 가능하다.
  • 직역하면 dbo 스키마의 캐릭터 테이블에서 LEVEL의 값이 4 이상인 것을 조회한다.

3번 - 아이템명이 gold인 아이템 정보를 보는 쿼리문

  • 2번과 마찬가지다.

4번 - 유저 id가 1인 유저의 아이템을 보는 쿼리문 - Join

SELECT I.*
- dbo.Item 테이블의 모든 컬럼만 결과로 내보냄. 결과 컬럼은 
- Item.ID, Item.OwnerCharacterID, Item.Type, Item.Amount
FROM dbo.UserAccount AS U
- 조회 기준 테이블을 dbo.UserAccount 로 잡고, 별칭 U로 대체
- U.ID , U.Email 처럼 짧게 참조 가능하다. 
JOIN dbo.[Character] AS C
- 기본적으로 INNER JOIN(교집합)을 수행.
- 정확한 연결 방식은 다음 줄 ON 에서 정해.
- dbo.[Character] 를 별칭 C로 대체.
- C.ID,C.Gender로 처럼 짧게 참조 가능하다.
ON C.OwnerUserAccountID = U.ID
- 의미 : Join 조건. 캐릭터의 (OwnerUserAccountID)FK와 유저의 (ID)PK와 같은 행끼리 붙여라
- ON절은 테이블을 어떻게 붙일치 정의하는 자리고, 필터는 Where에서. 
JOIN dbo.Item AS I

- 의미: 다시 INNER JOIN으로 지금까지 나온 행(= 유저↔캐릭터 매칭 결과)에 Item을 결합.

- 별칭 I: 이후 I.Type, I.Amount처럼 참조
ON I.OwnerCharacterID = C.ID

- 의미: 두 번째 조인 조건. 아이템의 소유 캐릭터 FK(I.OwnerCharacterID)가 캐릭터 PK(C.ID)와 같은 행만 붙여.

- 효과: 각 캐릭터가 실제로 보유한 아이템들만 결과에 남음.

- 주의(표현 보정): “캐릭터 ID와 아이템 ID 연결”이 아니라 아이템의 OwnerCharacterID와 캐릭터의 ID를 연결하는 거야.
- WHERE U.ID = 1;

- 의미: 조인된 결과에서 유저 ID가 1인 행만 남겨라.

- 타이밍(논리 처리 순서): SQL 논리적 처리 순서는 대략
- FROM → ON → (JOIN) → WHERE → SELECT → ORDER BY.
- 즉, 조인으로 붙이고 나서 그중 User=1만 필터링하는 거야.

- 결과 성질: INNER JOIN이므로, 유저 1번에게 캐릭터가 없거나 캐릭터에 아이템이 없으면 결과가 0행이 될 수 있어.
(아이템이 없어도 유저를 보고 싶다면 LEFT JOIN을 쓰면 됨.)

5번 - ItemID가 1인 아이템을 가지고 있는 유저를 가져오는 쿼리문

6번 - 캐릭터의 gender가 Male인 캐릭터를 보유하고 있는 유저를 가져오는 쿼리문

0개의 댓글