함수 종속(Functional Dependency)
정규화를 알아보기 전에 먼저 함수 종속을 알아야 한다. 함수 종속(FD)이란 한 테이블에 있는 두 개의 속성 집합 사이의 제약을 의미한다.
만약 특정 X 값에 따라서 Y 값이 유일하게 결정될 때 X가 Y를 함수적으로 결정한다라고 하고, 반대로 Y가 X에 함수적으로 의존한다고 말한다. 이러한 제약 관계를 함수 종속(FD)라 하며 기호로는 X -> Y로 표현한다.
함수 종속을 파악하기 위해서는 테이블의 스키마를 보고 의미적으로 파악해야 한다. 특정 상태에 따라서 파악하게 되면 중복되는 속성 값이 있을 수도 있기 때문에 상태가 아닌 스키마를 보고 함수 종속을 파악한다.
- 자명 함수 종속(Trivial Functional Dependency)
X가 Y를 결정할 때 Y가 X의 부분 집합이라면 X가 Y의 trivial FD이다. 즉, 당연히 성립될 수밖에 없는 함수적 종속을 의미한다.
예를 들어 아래와 같은 함수 종속이 있다고 생각해 보자.
X Y
{a, b, c} --> {c}
{a, b, c} --> {a, c}
{a, b, c} --> {a, b, c}
위와 같이 Y는 X의 부분 집합이기 때문에 Trivial FD이다.
만약 {a, b, c} --> {b, c, d} 이러한 함수 종속이 있다면 Y가 X의 부분 집합이 아니기 때문에 Non-Trivial FD라고 한다.
- 완전 함수 종속(Full Functional Dependecy)
완전 함수 종속(Full FD)은 X가 Y를 결정할 때 X 집합의 proper subset(진부분집합)이 Y를 결정할 수 없는 것을 완전 함수 종속이라고 한다.
예를 들어 아래와 같이 함수 종속이 있다고 가정해 보자.
X Y
{stu_id, class_id} --> {grade}
예시를 살펴보면 각각의 집합을 가지고 grade를 결정할 수 없다. 생각해 보면 stu_id(학생 아이디)만 가지고 어떤 수업을 듣는지 모르기 때문에 grade를 결정할 수 없고, 마찬가지로 class_id(수업 코드)만 가지고는 어떤 학생의 grade인지 결정할 수 없다.
즉, 두 가지의 키를 합쳐서 사용해야 grade를 결정할 수 있게 되는데 이러한 함수 종속을 완전 함수 종속(Full FD)이라고 한다.
※ proper subset(진부분집합)
proper subset이 뭔지 헷갈렸는데 다른 자료들도 찾아보니깐 조금 이해할 수 있었다.
proper subset은 X의 부분 집합이지만 X와는 동일하지 않는 집합을 의미하는데 예를 들면
X = {a, b, c}일 때 proper subset은 {a, c}, {a}, {}이다. 반면에 {a, b, c}는 X 집합과 완전히 동일하기 때문에 proper subset이 아니다.
- 부분 함수 종속(Partial Functional Dependency)
부분 함수 종속(Partial FD)은 X가 Y를 결정할 때 X 집합의 proper subset이 Y를 결정할 수 있다면 부분 함수 종속이라고 한다.
예를 들어 아래와 같은 함수 종속이 있다고 가정해 보자.
X Y
{emp_id, emp_name} --> {birth_date}
이전의 완전 함수 종속과는 다르게 굳이 emp_id와 emp_name을 같이 사용하지 않아도 emp_id만 가지고도 birth_date를 결정할 수 있다. 따라서 {emp_id, emp_name} --> {birth_date}와 같은 함수 종속을 부분 함수 종속(Partial FD)이라고 한다.
- 이행적 함수 종속(Transitive FD)
이행적 함수 종속(Trasitive FD)은 X가 Y를 결정하고, Y가 Z를 결정할 때 X가 Z를 결정할 수 있는 것을 이행적 함수 종속이라고 한다.
예를 들어 아래와 같은 함수 종속이 있다고 가정해 보자.
X Y Z
{bank_name, account_num} --> {emp_id}
{emp_id} --> {emp_name}
위의 예시와 같이 함수 종속이 있다고 생각해 보면 {bank_name, account_num}으로도 emp_name을 알 수 있게 된다. 이처럼 X를 통해 Z를 알 수 있는 것을 이행적 함수 종속(Transitive FD)이라고 한다.
여기서 주의할 점으로는 집합 Y와 Z는 키에 대해서 부분 집합이 아니어야 한다.
정규화(Normalization)
이제 정규화에 대해서 알아보면 정규화란 데이터의 중복과 이상 현상을 최소화하기 위해 일련의 정규화 과정에 따라 관계형 DB를 구성하는 과정을 의미한다.
※ 이상현상(Anomaly)
삽입 이상(Insertion Anomaly): 새로운 데이터를 삽입할 때 불필요한 데이터도 함께 삽입되는 현상을 삽입 이상이라고 한다.
갱신 이상(Update Anomaly): 중복된 데이터 중 일부만 변경이 되어 데이터 불일치가 발생하는 모순적인 현상을 갱신 이상이라 한다.
삭제 이상(Deletion Anomaly): 데이터를 삭제할 경우 필요한 데이터까지 함께 삭제되어 데이터 손실이 발생하는 현상을 삭제 이상이라 한다.
이러한 이상 현상은 데이터 중복으로 인해서 발생하기 때문에 DB를 처음 설계할 때부터 정규화 과정을 거쳐 중복이 발생하지 않게 DB를 구성해야 한다.
정규화를 수행하기 위해서는 속성들 간의 관련성을 파악해야 하는데 이 속성들 간의 관련성을 앞서 살펴봤던 함수 종속(Functional Dependency)이라고 하며 정규화되기 위해 준수해야 하는 각각의 규칙들을 NF(Normal Forms)라고 부른다.
정규화 과정은 처음 1NF부터 순차적으로 진행하며 normal form을 만족하지 못하면 만족하도록 테이블을 조정하고 다음 정규화 단계를 진행한다.
1NF와 BCNF까지 FD와 키 만으로 정규화를 진행하는데 보통 실무에서는 3NF나 BCNF까지 진행한다고 한다.
- 테이블 분석
예제를 통해 정규화를 알아보기 전에 먼저 테이블을 분석해야 한다.
candidate key(후보키): {account_id}, {bank_name, account_num}
primary key(기본키): {account_id}
prime attribute: account_id, bank_name, account_num
non-prime-attribute: class, ratio, emp_id, emp_name, card_id
- prime attribute: 후보 키에 포함된 속성 값들
- non-prime-attribute: 후보 키에 포함되지 않는 속성 값들
- FD 분석하기
테이블 분석이 끝났으면 이제 어떤 함수 종속이 있는지 분석해야 한다.
위의 테이블을 보고 함수 종속을 파악해 보면 아래와 같이 나온다.
X Y
{account_id} -> {bank_name, acount_num, class, ratio, emp_id, emp_name, card_id}
{bank_name, account_num} -> {account_id, class, ratio, emp_id, emp_name, card_id}
{emp_id} -> {emp_name}
{class} -> {bank_name}
class(등급)는 은행마다 달라진다 따라서 class를 통해 은행 이름을 알 수 있기 때문에 함수 종속이 생기게 된다.
- 제 1정규화(1NF)
1NF는 모든 속성 값은 반드시 나눠질 수 없는 단일한 값이어야 한다는 규칙이다.
예를 들어 위의 테이블과 같이 데이터가 저장되어 있다고 가정해 보자.
emp_name이 Jun인 사람의 card_id가 2개를 가지고 있기 때문에 1NF를 만족하지 못하고 있다. 따라서 해당 속성을 나눈다.
card_id 속성을 나누게 되면서 새로운 튜플이 추가되면서 1NF를 만족시키게 된다. 하지만 1NF를 만족하지만 속성 값을 나누게 되면서 Jun이 2개가 되는 중복 데이터가 생겼다. 이로 인해서 기존의 기본 키가 중복이 되는 문제가 발생한다.
또한, card_id가 새로운 기본 키가 되면서 모든 non-prime attribute들이 {account_id, card_id}에 부분 함수 종속이 된다.
즉, account_id 만으로도 튜플들을 유니크하게 결정할 수 있고, card_id로도 가능하게 되는 것이다.
- 제 2정규화(2NF)
2NF는 모든 non-prime attribute는 모든 키에 완전 함수 종속이어야 한다는 규칙이 있다.
위의 테이블을 보면 모든 non-prime attribute들은 {account_id, card_id}에 대해서 부분 함수 종속인 상태이다.
따라서 완전 함수 종속으로 만들기 위해 기본 키가 되는 card_id를 따로 분리해야 한다.
account_id와 card_id를 테이블로 따로 분리를 함으로써 기존 테이블에서는 account_id에 대한 완전 함수 종속 상태를 유지할 수 있게 된다.
- 제 3정규화(3NF)
3NF는 모든 non-prime attribute는 어떤 키에도 이행적 함수 종속(transitively FD)하면 안 된다는 규칙이 있다.
2 정규화까지 진행한 테이블을 살펴보면 테이블에서 이행적 함수 종속을 찾을 수 있다.
X Y Z
{bank_name, account_num} -> {emp_id}
{emp_id} -> {emp_name}
위와 같이 이행적 함수 종속으로 인해 {bank_name, account_num}가 {emp_name}을 결정할 수 있게 된다. 따라서 emp_id와 emp_name을 다른 테이블로 분리해 주면서 이행적 함수 종속을 해결해야 한다.
emp_id와 emp_name을 테이블로 분리하게 되면서 최종적으로 3NF가 완료되었다.
정규화를 공부해 보니 간단한 테이블 구조에서도 굉장히 어렵게 느껴졌다. 블로그에 정리하면서도 왜 이게 함수적 종속이지? 왜 이렇게 되는 거지? 이런 생각을 하면서 이해하기 어려웠고, 계속 강의를 보며 이해하려고 노력했다.
공부하면서 가장 크게 느꼈던 부분은 DB의 중요성이었다. DB에서 문제가 발생하면 해결하기 위한 비용도 만만치 않고 전체 서비스에 영향이 크기 때문에 최대한 문제가 발생하지 않도록 조심히 다뤄야 된다고 생각했다.
앞으로 내가 진행하는 프로젝트에서 정규화 과정을 직접 진행해 보고 다른 예제들도 참고하면서 연습을 충분히 해봐야겠다.
BCNF도 같이 공부했지만 아직 정확하게 이해하지 못해서 추후에 다시 공부해 보고 정리할 예정이다.
참고 자료
데이터베이스 정규화 - BCNF
개요 데이터베이스 정규화에서 BCNF 에 대해 알아본다.
yaboong.github.io
https://www.youtube.com/watch?v=EdkjkifH-m8&list=PLcXyemr8ZeoREWGhhZi5FZs6cvymjIBVe&index=23
'CS > 데이터베이스' 카테고리의 다른 글
데이터베이스 - 스키마 (0) | 2024.08.12 |
---|---|
데이터베이스 - 인덱스 (0) | 2024.08.08 |
데이터베이스 - 고립화 수준과 이상 현상 (2) | 2024.08.05 |
데이터베이스 - 동시성 제어 (0) | 2024.08.05 |
데이터베이스 - 트랜잭션 (0) | 2024.08.04 |