DB 정규화

정규화

정규화란 RDB 설계에서 이상 현상을 해결하고, 중복을 최소화한 데이터 구조를 설계하는 방법이다.

1~5차 정규형까지 있으며, 4차 정규형부터는 역효과가 발생할 수 있기 때문에 일반적으로는 3차 정규형까지만 사용한다. 

 

 

이상 현상이란?

잘못된 테이블 설계로 인해 갱신 이상, 삽입 이상, 삭제 이상 문제가 발생할 수 있다.

 

학번 이름 나이 성별  강의 코드 강의명 전화번호
1001 김철수 21 A12 Database 010-1234-1234
1002 이유리 21 A13 OS 010-3562-5674
1003 신짱구 23 A14 Data Structure 010-2345-6789
1003 신짱구 23 A15 Web Programming 010-2345-6789
1004 맹구 22 A16 Algorithm 010-5673-6951

 

갱신 이상: 중복된 데이터 중의 일부만 갱신되어 데이터의 불일치가 문제가 발생한다.

  • "3번 레코드 짱구"의 전화번호를 변경하게 되면, 3번과 4번은 같은 짱구지만 다른 전화번호가 저장된다.

삽입 이상: 자료를 삽입할 때 불필요한 데이터를 추가해야한다.

  • 강의를 수강하지 않은 "이훈이"라는 유저를 추가하면 강의 정보에 null을 넣어주어야 한다.

삭제 이상: 데이터 삭제 시, 의도하지 않은 데이터까지 삭제되는 문제가 발생한다.

  • "Database" 강의를 삭제하면 "철수"라는 유저도 삭제된다.

 

 

정규화의 장점

  • 스키마 변경이 정규화를 하지 않았을 때 보다 유연하다.
  • 중복이 적어 DB 저장 공간을 효율적으로 사용할 수 있다.
  • 이상 현상을 해결하여 올바른 데이터를 얻을 수 있다.
  • 애플리케이션에서 불필요한 로직을 줄일 수 있다.
  • 불필요한 쿼리를 제거할 수 있다.

 

정규화의 단점

  • 테이블이 여러 개로 쪼개지기 때문에 JOIN 연산이 많아져 조회 속도가 떨어질 수 있다.

 

 

반정규화

정규화는 더 보기 좋은 스키마와 중복이 제거되어 데이터 저장 공간을 덜 차지하여 성능이 좋아질 수도 있고, JOIN이 필요한 테이블이 많아질 수록 성능이 떨어질 수도 있다. 즉, 정규화가 무조건 정답은 아니다.

이런 상황에서 정규화와 반대로 중복을 허용하고 JOIN을 줄이는 방법반정규화이다.

 

반정규화가 필요한 예시 상황

상품, 주문, 고객 테이블이 존재할 때 주문 내역 페이지주문 내역과 주문에 속한 상품 정보항상 같이 보이고 요청 빈도가 높다면, 조인을 사용하는 것이 성능을 떨어트리는 요인이 될 수 있다.
이런 경우 주문 테이블에 상품 정보를 넣어 조인을 하지않는 방법을 고려해볼 수 있다.

 

1차 정규화(1NF)

  • 각 컬럼이 하나의 속성만을 가진다.
  • 하나의 컬럼은 같은 종류나 타입을 가져야한다.
  • 각 컬럼이 유일한 이름을 가져야한다.
  • 칼럼의 순서가 상관없어야한다.
// 1차 정규화 전
이름 | 나이 |  과목  |
짱구 | 25  | C,C++ |

// 1차 정규화 후
이름 | 나이 | 과목 |
짱구 | 25  | C   |
짱구 | 25  | C++ |

 

 

2차 정규화(2NF)

1차 정규화를 진행한 테이블에 대해 완전 함수 종속을 만족해야한다.

  • 1차 정규화를 만족
  • 모든 컬럼은 부분 함수 종속이 없어야한다.
  • 모든 컬럼은 완정 함수 종속을 만족해야한다.
// 2차 정규화 전
/*
   부분 함수 종속 = 이름과 과목이 복합 키로 기본 키이고, 이름만 알면 나이를 알 수 있다.
   따라서 과목 정보를 볼 때 이름과 나이가 같이 저장될 필요가 없다.
*/
| 이름 | 나이 |  과목 |    
| 짱구 | 25  |  C   |    
| 짱구 | 25  | C++  |
| 철수 | 21  | Java |


// 2차 정규화 후: 짱구는 무조건 25살 이기 때문에 완전 함수 종속이며, 복합 키 부분 종속이 없어졌다.
/* 
  완전 함수 종속 = 이름: x, 나이: y 일 때 x는 무조건 y여야한다.
  이름을 통해 나이와 과목을 결정할 수 있다.
*/
| 이름 | 나이 |      | 이름 |  과목 |	
| 짱구 | 25  |      | 짱구 |  C   |
| 철수 | 21  |      | 짱구 | C++  |
                   | 짱구 | Java |

 

완전 함수 종속

모든 컬럼이 기본 키에만 종속되는 경우 (X->Y) X: 기본 키

아래 예시에서는 주민번호를 알면 이름, 성별, 주소를 모두 알 수 있다.

 

부분 함수 종속

기본 키복합 키일 경우 기본 키를 구성하는 속성 중 일부에게 종속된 경우 (X+Y -> Z && X->Z) X+Y: 복합키, X: 복합키 구성 요소

아래 예시는 토니 스타크는 성별에 따라 2명이 존재하기 때문에 이름+성별기본 키이며, 주소 지역번호를 알 수 있다.

하지만, 기본 키를 구성하는 속성 중 일부이름만 알더라도 주소지역번호를 알 수 있다.

 

 

 

 

3차 정규화(3NF)

  • 2차 정규화를 만족해야한다.
  • 기본 키를 제외한 속성들 간의 이행 종속성이 없어야한다.
//3차 정규화 전: 장영과 우영이 동일한 Zip Code를 가져 도시 정보 또한 같이 쌓이게 된다.
| 이름 | 나이 | 도시 | Zip Code |
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
| 장영 | 25  | 제주 | 01234    |
| 우영 | 22  | 제주 | 01234    |
| 이진 | 25  | 서울 | 56789    |
| 주혜 | 24  | 부산 | 13579    |
| 정희 | 24  | 강릉 | 24681    |

//3차 정규화 후: 사람과 주소로 테이블을 분리하여 중복을 제거하였다.
| 이름 | 나이 |      | 도시 | Zip Code |
ㅡㅡㅡㅡㅡㅡㅡㅡㅡ      ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
| 장영 | 25  |      | 제주 | 01234    |
| 우영 | 22  |      | 서울 | 56789    |
| 이진 | 25  |      | 부산 | 13579    |
| 주혜 | 24  |      | 강릉 | 24681    |
| 정희 | 24  |

 

이행 함수 종속

X, Y, Z 속성이 있을 때 X를 통해 Y를 알 수 있고, Y를 통해 Z를 알 수 있는 경우 (X->Y, Y->Z 일 때 X->Z) 

 

아래 예시는 이름 + 나이를 통해 Zip Code를 알 수 있고, Zip Code를 통해서 도시를 알 수 있다. 

 

 

 

 

BCNF(Boyce-Codd Normal Form)

  • 3차 정규화를 만족해야한다.
  • 모든 결정자후보 키가 되어야한다. (X->Y) X: 결정자

 

아래 테이블의 X->Y는 "(이름 + 과목) -> 교수님",  "교수님 -> 과목"이다.

 

 

이름+과목을 알고있을 때 교수님과 학점을 알 수 있지만, 교수님을 알고있을 때는 과목만 알 수 있다. 따라서 아래와 같이 분리할 수 있다.