# 1NF, 2NF

사람들이 SQL 배울 때 JOIN 문법을 매우 어려워합니다.&#x20;

그거 배우다보면 의문도 많이 생기고 왜 테이블을 애초에 여러개로 나눠놓는지 이해도 안가고 약간 반발심이 생길텐데 지금 설명할 정규화/정규형이라는걸 배우게 되면 매우 쉬워집니다.&#x20;

배워두면 나중에 DB 설계도 도움없이 알아서 잘할 수 있음&#x20;

### **제1정규형 (1st normal form)**

여러분은 지금부터 체육센터 전산시스템 만드는 개발자입니다.&#x20;

일단 체육센터에 수강 등록한 사람들을 테이블로 정리하고 싶은겁니다.&#x20;

**\[수강등록현황 table]**

| 회원번호 | **회원이름** | **프로그램** |
| ---- | -------- | -------- |
| 101  | 강호동      | 스쿼시초급    |
| 102  | 손흥민      | 헬스       |
| 103  | 김민수      | 헬스       |

그래서 테이블 하나 만들어서 이쁘게 잘 저장을 해놨습니다.&#x20;

근데 '김민수' 라는 사람이 다음 날 '배드민턴초급'을 또 수강신청한겁니다.

그럼 어떻게 데이터를 추가하죠?

&#x20;

| 회원번호 | **회원이름** | **프로그램** |
| ---- | -------- | -------- |
| 101  | 강호동      | 스쿼시초급    |
| 102  | 손흥민      | 헬스       |
| 103  | 김민수      | 헬스, 골프초급 |

하나의 셀에 데이터를 더 추가하면 어떨까요.&#x20;

그래도 되긴 합니다.

&#x20;

근데 보통은 이렇게 해놓으면 나중에 성능 이슈가 생길 수 있습니다.

저렇게 문자로 길게 저장해두면 상품목록이 길어질 수록 찾기 / 수정 / 삭제 작업이 느려집니다.&#x20;

저렇게 해놓으면 사수가 면담시간 가지자고 함

실은 JSON 데이터타입 아니면 array 데이터타입 (Postgres에서 가능) 사용하면

**\['헬스', '골프초급']**

이런 식으로 여러개의 문자나 숫자를 한 칸에 저장할 수 있는데

그거 써도 마찬가지로 문제될 수 있습니다.&#x20;

왜냐면 그냥 array, JSON은 문자와 취급이 비슷해서 나중에 array 자료 안의 **일부 항목만** **수정하는게 매우 어렵기** **때문**입니다.&#x20;

(그래서 수정할 때는 새 값으로 아예 갈아치우거나 그런 식으로 많이 합니다)

| 회원번호 | **회원이름** | **프로그램** |
| ---- | -------- | -------- |
| 101  | 강호동      | 스쿼시초급    |
| 102  | 손흥민      | 헬스       |
| 103  | 김민수      | 헬스       |
| 103  | 김민수      | 골프초급     |

▲ 그래서 이렇게 만드는게 낫습니다.

**하나의 셀 안에는 하나의 데이터만** 보관하는 겁니다.

이 작업이 완료된 테이블을 제1정규형 (1st normal form) 이라고 합니다.

이렇게 해두면 성능문제도 없어지고 나중에 다른 컬럼을 추가할 때도 문제가 안생길 수 있습니다.

(참고)

예전에 배운 비관계형 데이터베이스들은 제1 정규화를 안하는 경우가 있습니다.&#x20;

하지만 관계형 데이터베이스는 제1 정규화 안해놓으면 단점이 많아서 데이터 저장할 때 항상 하도록 합시다.

&#x20;

### **제2정규형 (2nd normal form)**

<table data-header-hidden><thead><tr><th></th><th width="146"></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>회원번호</td><td><strong>회원이름</strong></td><td><strong>프로그램</strong></td><td><strong>가격</strong></td><td><strong>납부여부</strong></td></tr><tr><td>101</td><td>강호동</td><td>스쿼시초급</td><td>5000</td><td>0</td></tr><tr><td>102</td><td>손흥민</td><td>헬스</td><td>6000</td><td>1</td></tr><tr><td>103</td><td>김민수</td><td>헬스</td><td>6000</td><td>1</td></tr><tr><td>103</td><td>김민수</td><td>골프초급</td><td>8000</td><td>0</td></tr></tbody></table>

아까 제1정규화 해놓은 테이블이 있습니다.&#x20;

심심해서 각 프로그램마다 '가격'이랑 '납부여부'를 기록하는 컬럼도 만들어놨습니다.&#x20;

잘 돌아갈 것 같은데 근데 이런 문제가 생길 수도 있습니다.

&#x20;

**Q. '헬스' 프로그램의 가격에 오류가 있어서 6000에서 7000으로 변경되어야하면 어떻게 할까요?**&#x20;

\- 2개 각각 수정하면 됩니다.

**Q. 근데 회원수가 늘어나서  '헬스'를 신청한 사람이 100명이면 어쩌죠?**&#x20;

100개 행을 고치는 불상사가 생기는게 싫다면&#x20;

**현재 테이블의 주제와 관련없는 컬럼은 다른 테이블로** 옮기십시오.

**\[수강등록현황 table]**

<table data-header-hidden><thead><tr><th width="178"></th><th width="152"></th><th></th><th></th></tr></thead><tbody><tr><td>회원번호</td><td><strong>회원이름</strong></td><td><strong>프로그램</strong></td><td><strong>납부여부</strong></td></tr><tr><td>101</td><td>강호동</td><td>스쿼시초급</td><td>0</td></tr><tr><td>102</td><td>손흥민</td><td>헬스</td><td>1</td></tr><tr><td>103</td><td>김민수</td><td>헬스</td><td>1</td></tr><tr><td>103</td><td>김민수</td><td>골프초급</td><td>0</td></tr></tbody></table>

**\[프로그램 table]**

<table data-header-hidden><thead><tr><th width="326"></th><th></th></tr></thead><tbody><tr><td><strong>프로그램</strong></td><td><strong>가격</strong></td></tr><tr><td>스쿼시초급</td><td>5000</td></tr><tr><td>헬스</td><td>6000</td></tr><tr><td>골프초급</td><td>8000</td></tr></tbody></table>

'가격' 컬럼은 수강등록현황과 별로 상관이 없고 '프로그램' 종류에 따라서 결정될 뿐입니다.

그래서 '가격' 컬럼은 현재 테이블의 주제와 맞지 않기 때문에

그걸 잘라내서 새로운 테이블 만들어서 거기 넣어봤습니다.  &#x20;

이 작업이 완료된 테이블을 제2정규형 (2nd normal form) 이라고 합니다.

장점은 아까처럼 '헬스'의 가격이 수정되어야할 때 100곳을 수정할 필요가 없어짐&#x20;

&#x20;

물론 이러면 단점도 있는데 나중에 김민수라는 사람이 얼마를 내야할지 조회하는게 귀찮아질 수 있는데

그건 나중에 SQL JOIN 문법 쓰면 됩니다.&#x20;

### 제2정규형 - 교과서버전

제2정규형의 교과서적 정의는 partial dependency를 제거한 테이블입니다.&#x20;

쉽게 설명해보자면&#x20;

1\. composite primary key 라는게 있습니다.

primary key는 행을 서로 구분할 수 있는 유니크한 데이터를 담고있는 컬럼입니다.&#x20;

근데 가끔은 하나의 컬럼만으로 primary key를 정할 수 없는 경우가 있습니다.&#x20;

&#x20;

<table data-header-hidden><thead><tr><th width="134"></th><th width="138"></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>회원번호</td><td><strong>회원이름</strong></td><td><strong>프로그램</strong></td><td><mark style="color:orange;"><strong>가격</strong></mark></td><td><strong>납부여부</strong></td></tr><tr><td><mark style="color:blue;">101</mark></td><td>강호동</td><td><mark style="color:blue;">스쿼시초급</mark></td><td>5000</td><td>0</td></tr><tr><td><mark style="color:blue;">102</mark></td><td>손흥민</td><td><mark style="color:blue;">헬스</mark></td><td>6000</td><td>1</td></tr><tr><td><mark style="color:blue;">103</mark></td><td>김민수</td><td><mark style="color:blue;">헬스</mark></td><td>6000</td><td>1</td></tr><tr><td><mark style="color:blue;">103</mark></td><td>김민수</td><td><mark style="color:blue;">골프초급</mark></td><td>8000</td><td>0</td></tr></tbody></table>

▲ 위의 테이블에선 (회원번호 + 프로그램) 이렇게 조합해야 primary key 역할을 수행할 수 있습니다.

'회원번호' 만으로 primary key 역할은 불가능합니다.

'프로그램' 만으로 primary key 역할은 불가능합니다.&#x20;

근데 두개 합치면 primary key 역할 가능&#x20;

두개 컬럼을 합친 primary key를 composite primary key 라고 합니다.&#x20;

&#x20;

\
2\. composite primary key 중에 하나의 컬럼에만 종속되어 있는 따까리 컬럼을

partial dependency가 있다고 표현합니다.&#x20;

3\. partial dependency가 있는 컬럼을 다른 테이블로 빼면 제2정규형 테이블 완성입니다.&#x20;

그래서 위의 예시의 '가격' 컬럼을 다른 테이블로 빼는 것입니다.&#x20;

<table data-header-hidden><thead><tr><th width="135"></th><th width="144"></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>회원번호</td><td><strong>회원이름</strong></td><td><strong>프로그램</strong></td><td><strong>가격</strong></td><td><strong>준비물</strong></td></tr><tr><td>101</td><td>강호동</td><td>스쿼시초급</td><td>5000</td><td>라켓</td></tr><tr><td>102</td><td>손흥민</td><td>헬스</td><td>6000</td><td>없음</td></tr><tr><td>103</td><td>김민수</td><td>헬스</td><td>6000</td><td>없음</td></tr><tr><td>103</td><td>김민수</td><td>골프초급</td><td>8000</td><td>골프채</td></tr></tbody></table>

**Q1. 위 테이블에서 '준비물' 컬럼은 partial dependency가 있을까요 없을까요?**

<table data-header-hidden><thead><tr><th></th><th></th><th></th><th width="134"></th><th></th></tr></thead><tbody><tr><td>회원번호</td><td><strong>회원이름</strong></td><td><strong>프로그램</strong></td><td><strong>가격</strong></td><td><strong>납부여부</strong></td></tr><tr><td>101</td><td>강호동</td><td>스쿼시초급</td><td>5000</td><td>0</td></tr><tr><td>102</td><td>손흥민</td><td>헬스</td><td>6000</td><td>1</td></tr><tr><td>103</td><td>김민수</td><td>헬스</td><td>6000</td><td>1</td></tr><tr><td>103</td><td>김민수</td><td>골프초급</td><td>8000</td><td>0</td></tr></tbody></table>

**Q2. 위 테이블에서 '돈납부여부' 컬럼은 partial dependency가 있을까요 없을까요?**

(팁) 종속관계 판단이 어렵다면 하나의 컬럼을 다른 값으로 바꿔보십시오.

그 경우 다른 컬럼도 변경되어야한다면 그 관계를 종속관계라고 합니다.

#### **partial dependency 설명 링크**

{% embed url="<https://www.tutorialspoint.com/Partial-Dependency-in-DBMS>" %}

✨ 실습.&#x20;

<table data-header-hidden><thead><tr><th></th><th></th><th></th><th width="144"></th><th></th><th></th></tr></thead><tbody><tr><td><strong>회원명</strong></td><td><strong>책이름</strong></td><td><strong>날짜</strong></td><td><strong>회원등급</strong></td><td><strong>책가격</strong></td><td><strong>반납여부</strong></td></tr><tr><td>lee</td><td>디자인책</td><td>1월1일</td><td>우수</td><td>1000</td><td>1</td></tr><tr><td>kim</td><td>만화책</td><td>1월2일</td><td>일반</td><td>2000</td><td>0</td></tr><tr><td>park</td><td>SQL책</td><td>1월3일</td><td>일반</td><td>1000</td><td>1</td></tr><tr><td>park</td><td>소설책</td><td>1월3일</td><td>일반</td><td>2000</td><td>0</td></tr></tbody></table>

**Q1. 위 테이블에서 책가격 컬럼은 다른 테이블로 옮기는게 좋을까요?**

**Q2. 위 테이블에서 회원등급 컬럼은 다른 테이블로 옮기는게 좋을까요?**

**Q3. 위 테이블에서 반납여부 컬럼은 다른 테이블로 옮기는게 좋을까요?**

※ 정규화 설명 참조 블로그

{% embed url="<https://inpa.tistory.com/entry/DB-%F0%9F%93%9A-%EC%A0%9C-1-2-3-%EC%A0%95%EA%B7%9C%ED%99%94-%EC%97%AD%EC%A0%95%EA%B7%9C%ED%99%94>" %}
