Back to Notes

Notes

DB 07. 정규화와 정규형

릴레이션 정규화의 목적, 함수적 종속성, 무손실 분해, 1NF·2NF·3NF·BCNF, 역정규화를 정리한 데이터베이스 학습 노트

Published
Updated
Area
Databases
Type
concept
Series
Database System
Category
Notes
databasenormalizationfunctional-dependencynormal-formBCNFdenormalization

릴레이션 정규화 개요

Normalization은 주어진 relation schema를 functional dependencykey를 기준으로 분석하고, 필요하면 relation을 분해하여 data redundancyupdate anomaly를 줄이는 설계 과정이다.

나쁜 database schema는 서로 다른 종류의 정보를 하나의 relation에 섞어 저장한다. 그 결과 같은 정보가 여러 tuple에 반복되고, 데이터 수정·삽입·삭제 과정에서 불일치가 발생하기 쉽다.

정규화의 핵심 목적은 다음과 같다.

  • 중복 저장을 줄인다.
  • 수정 이상, 삽입 이상, 삭제 이상을 줄인다.
  • 정보 손실 없이 relation을 분해한다.
  • attribute 사이의 의미적 관계를 schema에 반영한다.
  • integrity constraint를 더 단순하게 유지한다.
  • 성능은 정규화 이후 필요에 따라 별도로 검토한다.

좋은 설계의 기본 순서는 먼저 update anomaly를 줄이고, 이후 실제 workload를 기준으로 성능을 고려하는 것이다.


Update Anomaly

Update anomaly는 relation의 중복 구조 때문에 데이터 변경 과정에서 생기는 이상 현상이다. 여기서 update는 좁은 의미의 수정만이 아니라, insertion과 deletion까지 포함하는 넓은 의미로 이해한다.

종류의미대표 상황
Modification anomaly반복된 데이터 중 일부만 수정되어 불일치가 생김같은 부서명이 여러 tuple에 저장되어 있는데 일부만 변경
Insertion anomaly불필요한 다른 정보 없이는 원하는 정보를 삽입할 수 없음신설 부서가 있지만 아직 사원이 없어 부서 정보를 저장하지 못함
Deletion anomaly삭제하려던 정보와 함께 보존해야 할 정보까지 삭제됨마지막 사원을 삭제했더니 부서 정보도 함께 사라짐

예시: 사원 relation

초기 설계가 다음과 같다고 하자.

사원(사원번호, 사원이름, 주소, 전화번호, 부서번호, 부서이름)

이 relation에는 두 종류의 정보가 섞여 있다.

  • 사원 정보: 사원번호, 사원이름, 주소, 전화번호
  • 부서 정보: 부서번호, 부서이름

이 구조에서는 같은 부서에 여러 사원이 있으면 부서이름이 여러 번 반복된다. 따라서 부서명이 변경될 때 관련 tuple을 모두 수정해야 하며, 일부만 수정하면 데이터 불일치가 생긴다.

이를 다음처럼 분해하면 anomaly를 줄일 수 있다.

사원1(사원번호, 사원이름, 주소, 전화번호, 부서번호)
부서(부서번호, 부서이름)

이제 부서이름부서 relation에 한 번만 저장된다. 부서명 변경 시 한 행만 수정하면 되고, 아직 사원이 없는 신설 부서도 부서 relation에 삽입할 수 있다.


Functional Dependency

Functional Dependency, 줄여서 FD, 는 정규화 이론의 핵심이다. 표기는 다음과 같다.

A → B

이는 A 값이 정해지면 B 값이 하나로 결정된다는 뜻이다. 이때 Adeterminant, 즉 결정자라고 한다.

정확한 의미는 다음과 같다.

relation R에서 A → B가 성립하려면, A 값이 같은 두 tuple은 반드시 B 값도 같아야 한다.

예를 들어 사원번호가 사원을 고유하게 식별한다면 다음 FD가 성립한다.

사원번호 → 사원이름
사원번호 → 주소
사원번호 → 전화번호

부서번호가 부서를 고유하게 식별한다면 다음도 성립한다.

부서번호 → 부서이름

FD는 instance가 아니라 schema의 의미에 대한 주장

FD는 현재 relation instance에 우연히 들어 있는 값만 보고 판단하면 안 된다. 예를 들어 현재 데이터에 동명이인이 없다고 해서 이름 → 학번이 성립한다고 단정할 수 없다.

FD는 다음을 기준으로 판단해야 한다.

  • 실세계의 의미
  • 업무 규칙
  • application domain에서 항상 만족해야 하는 제약
  • 가능한 모든 relation instance

즉, 현재 데이터에서 우연히 중복이 없다는 사실은 FD의 근거가 되지 않는다.


완전 함수적 종속성과 부분 함수적 종속성

Full Functional Dependency

Full Functional Dependency, 즉 완전 함수적 종속성은 determinant가 composite attribute일 때 중요하다.

(학번, 과목번호) → 학점

이 FD가 성립하고, 다음은 성립하지 않는다고 하자.

학번 → 학점
과목번호 → 학점

이 경우 학점(학번, 과목번호)완전 함수적으로 종속한다.

핵심은 다음이다.

composite key 전체가 있어야 non-key attribute를 결정할 수 있으면 full functional dependency이다.

Partial Functional Dependency

Partial Functional Dependency, 즉 부분 함수적 종속성은 composite key 전체가 아니라 일부 attribute만으로 non-key attribute가 결정되는 경우다.

예를 들어 relation의 기본 키가 (학번, 과목번호)인데 다음 FD가 성립한다고 하자.

학번 → 학과이름
학번 → 학과전화번호

학과이름학과전화번호는 기본 키 전체가 아니라 기본 키의 일부인 학번에만 의존한다. 따라서 이는 partial dependency이다.

정리하면 다음과 같다.

구분판단 기준예시
Full functional dependencycomposite key 전체가 필요함(학번, 과목번호) → 학점
Partial functional dependencycomposite key의 일부만으로 결정됨학번 → 학과이름

주의할 점은 “기본 키 전체가 결정자”라는 표현이다. 이는 composite key의 각 attribute가 따로따로 결정자라는 뜻이 아니라, key 묶음 전체가 determinant로 작용한다는 뜻이다.


Transitive Functional Dependency

Transitive FD는 다음 구조를 말한다.

A → B
B → C

이때 결과적으로 다음도 성립한다.

A → C

예를 들어 학생 relation에서 다음 FD가 있다고 하자.

학번 → 학과이름
학과이름 → 학과전화번호

그러면 다음도 성립한다.

학번 → 학과전화번호

하지만 학과전화번호는 학생 자체의 속성이라기보다 학과의 속성이다. 따라서 학생(학번, 학과이름, 학과전화번호) 형태로 저장하면 같은 학과에 속한 학생 수만큼 학과 전화번호가 반복된다.

이 구조는 3NF에서 제거 대상이 된다.


Relation Decomposition

Relation decomposition은 하나의 relation을 둘 이상의 relation으로 나누는 것이다. 정규화는 relation decomposition을 통해 중복과 anomaly를 줄인다.

예를 들어 다음 relation이 있다고 하자.

학생(학번, 이름, 이메일, 과목번호, 학점)

다음 FD가 성립한다고 볼 수 있다.

학번 → 이름, 이메일
이메일 → 학번, 이름
(학번, 과목번호) → 학점

이 relation은 학생 정보와 수강 정보가 섞여 있다. 따라서 다음처럼 분해할 수 있다.

학생1(학번, 이름, 이메일)
수강(학번, 과목번호, 학점)

이렇게 하면 학생 이름과 이메일이 수강 과목 수만큼 반복 저장되지 않는다.

Lossless Decomposition

좋은 decomposition은 lossless decomposition이어야 한다.

분해된 relation들을 join한 결과 = 원래 relation

여기서 loss는 정보가 줄어드는 경우만 의미하지 않는다. Join 결과가 원래 relation보다 많아져서 원래 없던 tuple이 생기는 것도 정보 손실에 포함된다.

Spurious Tuple

잘못된 decomposition은 spurious tuple, 즉 가짜 tuple을 만든다.

예를 들어 원래 relation이 다음과 같다고 하자.

수강(학번, 과목번호, 학점)

올바른 FD는 다음이다.

(학번, 과목번호) → 학점

그런데 이를 다음처럼 분해하면 문제가 생긴다.

수강1(학번, 과목번호)
수강2(학번, 학점)

학점학번만으로 결정되지 않는다. 같은 학생이 여러 과목을 들으면 여러 학점이 존재할 수 있기 때문이다. 따라서 수강1수강2학번으로 join하면 과목과 학점의 모든 조합이 만들어져 원래 없던 tuple이 생긴다.

좋은 decomposition의 검증 포인트는 다음이다.

  1. 원래 relation의 FD를 확인한다.
  2. 분해 후 공통 attribute가 충분한 determinant 역할을 하는지 확인한다.
  3. Join 결과가 원래 relation과 정확히 같은지 확인한다.
  4. 원래 없던 tuple이 생성되지 않는지 확인한다.

Normal Forms

정규형은 relation이 어느 정도까지 anomaly를 줄이는 구조로 정리되었는지를 나타낸다.

Normal Form핵심 조건제거 대상
1NF모든 attribute가 atomic value를 가짐반복 그룹, 비원자값
2NF1NF를 만족하고 partial dependency가 없음부분 함수적 종속성
3NF2NF를 만족하고 transitive dependency가 없음이행적 종속성
BCNF모든 determinant가 candidate key후보 키가 아닌 결정자

First Normal Form

First Normal Form, 즉 1NF의 조건은 다음이다.

relation의 모든 attribute가 atomic value만 가져야 한다.

예를 들어 다음 relation은 1NF를 만족하지 않는다.

학생(학번, 이름, 과목번호, 주소)
학번이름과목번호주소
11002이흥근{CS310, CS313}우이동
24036김순미{CS310, CS345}양재동

과목번호 attribute에 set이 들어 있으므로 atomic value가 아니다.

이를 1NF로 바꾸는 방법은 두 가지가 있다.

방법 1: 각 값을 개별 tuple로 풀기

학번이름과목번호주소
11002이흥근CS310우이동
11002이흥근CS313우이동
24036김순미CS310양재동
24036김순미CS345양재동

이 방식은 1NF는 만족하지만 학생 정보가 반복된다.

방법 2: 반복 그룹을 별도 relation으로 분리

학생1(학번, 이름, 주소)
수강(학번, 과목번호)

이 방식은 학생 정보와 수강 정보를 의미 단위로 분리하므로 더 적절하다.


Second Normal Form

Second Normal Form, 즉 2NF는 다음 조건을 만족해야 한다.

1NF를 만족하고, key가 아닌 모든 attribute가 기본 키 전체에 full functional dependency를 가져야 한다.

즉, 2NF의 핵심은 partial dependency 제거다.

예를 들어 다음 relation을 보자.

학생(학번, 학과이름, 학과전화번호, 과목번호, 학점)

기본 키가 (학번, 과목번호)라고 할 때 다음 FD가 성립한다.

학번 → 학과이름
학번 → 학과전화번호
(학번, 과목번호) → 학점

학점(학번, 과목번호) 전체에 의존하므로 문제없다. 그러나 학과이름학과전화번호학번에만 의존한다. 이는 partial dependency이다.

2NF로 만들려면 다음처럼 분해한다.

학생1(학번, 학과이름, 학과전화번호)
수강(학번, 과목번호, 학점)

2NF 판단 기준

기본 키가 단일 attribute이면 partial dependency가 발생하지 않는다. 따라서 2NF는 주로 composite key를 가진 relation에서 확인한다.

검증 질문은 다음이다.

key가 아닌 attribute가 composite key 전체에 의존하는가?
아니면 key의 일부만으로 결정되는가?

일부만으로 결정되면 2NF 위반이다.


Third Normal Form

Third Normal Form, 즉 3NF는 다음 조건을 만족해야 한다.

2NF를 만족하고, key가 아닌 모든 attribute가 기본 키에 transitive dependency를 가지지 않아야 한다.

예를 들어 2NF를 만족하는 relation이 다음과 같다고 하자.

학생1(학번, 학과이름, 학과전화번호)

FD는 다음과 같다.

학번 → 학과이름
학과이름 → 학과전화번호

학과전화번호학번에 직접 종속되는 것처럼 보이지만, 실제로는 학과이름을 거쳐 종속된다. 즉, transitive dependency이다.

3NF로 만들려면 다음처럼 분해한다.

학생2(학번, 학과이름)
학과(학과이름, 학과전화번호)

이제 학과 전화번호는 학과 relation에 한 번만 저장된다.

3NF 판단 기준

3NF에서는 다음 구조를 찾으면 된다.

기본 키 → 일반 attribute → 다른 일반 attribute

이 구조가 있으면 key가 아닌 attribute가 다른 key가 아닌 attribute를 결정하는 것이므로 3NF 위반이다.


BCNF

BCNF, 즉 Boyce-Codd Normal Form은 3NF보다 강한 정규형이다.

핵심 조건은 다음이다.

모든 determinant는 candidate key이어야 한다.

즉, 어떤 FD X → Y가 성립한다면 X는 반드시 candidate key이어야 한다.

3NF는 만족하지만 BCNF는 위반하는 예시

다음 relation을 보자.

수강(학번, 과목, 강사)

조건은 다음과 같다.

  • 각 학생은 여러 과목을 수강할 수 있다.
  • 각 강사는 한 과목만 가르친다.

FD는 다음과 같다.

(학번, 과목) → 강사
강사 → 과목

후보 키는 다음 두 개다.

(학번, 과목)
(학번, 강사)

강사 → 과목에서 determinant는 강사이다. 그러나 강사 하나만으로는 relation의 tuple을 고유하게 식별할 수 없다. 여러 학생이 같은 강사의 수업을 들을 수 있기 때문이다.

따라서 강사는 candidate key가 아니지만 determinant 역할을 한다. 이 relation은 3NF는 만족할 수 있지만 BCNF는 만족하지 않는다.

BCNF로 분해

문제가 되는 FD는 다음이다.

강사 → 과목

따라서 determinant와 dependent attribute를 별도 relation으로 분리한다.

수강1(학번, 강사)
수강2(강사, 과목)

수강2에서 강사는 기본 키가 된다.

강사 → 과목

이제 determinant인 강사가 해당 relation의 key이므로 BCNF 조건을 만족한다.


3NF와 BCNF의 구분 기준

3NF와 BCNF는 다음 기준으로 구분한다.

항목3NFBCNF
핵심 관심key가 아닌 attribute 사이의 transitive dependency모든 determinant가 candidate key인지
강도상대적으로 약함더 강함
대표 위반 구조기본 키 → 일반 attribute → 일반 attributecandidate key가 아닌 determinant → attribute
판단 질문non-key attribute가 다른 non-key attribute를 결정하는가?FD의 왼쪽이 candidate key인가?

BCNF 문제를 풀 때는 다음 순서가 유용하다.

  1. 모든 FD를 나열한다.
  2. candidate key를 찾는다.
  3. 각 FD의 왼쪽 determinant가 candidate key인지 확인한다.
  4. candidate key가 아닌 determinant가 있으면 BCNF 위반이다.
  5. 위반 FD를 기준으로 relation을 분해한다.

Denormalization

Denormalization, 즉 역정규화는 성능을 위해 정규화된 relation을 의도적으로 다시 합치는 것이다.

정규화는 중복과 anomaly를 줄이지만 relation이 많이 분해될수록 join이 증가한다. 높은 정규형이 항상 성능 관점에서 최적인 것은 아니다.

예를 들어 정규화된 구조가 다음과 같다고 하자.

학생2(학번, 학과이름)
학과(학과이름, 학과전화번호)

학번이 11002인 학생의 학과 이름과 전화번호를 조회하려면 join이 필요하다.

SELECT 학과이름, 학과전화번호
FROM 학생2, 학과
WHERE 학번 = '11002'
  AND 학생2.학과이름 = 학과.학과이름;

반면 역정규화된 구조에서는 다음 relation 하나로 조회할 수 있다.

학생1(학번, 학과이름, 학과전화번호)
SELECT 학과이름, 학과전화번호
FROM 학생1
WHERE 학번 = '11002';

Trade-off

구분정규화역정규화
목적data consistencyquery performance
방식relation decompositionrelation merge
장점중복 감소, update anomaly 감소join 감소, 조회 성능 향상 가능
단점join 증가중복 증가, update anomaly 가능성 증가
적용 시점기본 설계 단계workload 분석 후 제한적으로 적용

역정규화는 나쁜 설계로의 무분별한 회귀가 아니라, 성능 요구사항을 만족시키기 위한 의도적 타협이다. 다만 중복 데이터의 일관성을 유지할 수 있는 보완책이 필요하다.


Common Pitfalls

1. 현재 데이터만 보고 FD를 판단하는 경우

현재 instance에서 값이 우연히 중복되지 않는다고 해서 FD가 성립하는 것은 아니다. FD는 가능한 모든 instance가 만족해야 하는 schema-level 제약이다.

2. 1NF를 만족하면 정규화가 끝났다고 보는 경우

1NF는 atomic value 조건만 만족한다. 1NF 이후에도 partial dependency, transitive dependency, non-key determinant 문제가 남을 수 있다.

3. Composite key의 일부 종속을 놓치는 경우

기본 키가 (A, B)일 때 A → C가 성립하면 C는 key 전체가 아니라 일부에만 의존한다. 이는 2NF 위반이다.

4. 3NF와 BCNF를 동일하게 보는 경우

대부분의 3NF relation은 BCNF도 만족할 수 있지만 항상 그런 것은 아니다. 강사 → 과목처럼 candidate key가 아닌 determinant가 있으면 BCNF 위반이다.

5. 무조건 많이 분해하는 경우

정규화는 의미 있는 anomaly를 줄이기 위한 분해이지, relation 수를 무조건 늘리는 작업이 아니다. 분해 후에는 lossless decomposition 여부와 join cost를 함께 고려해야 한다.


Summary

릴레이션 정규화의 전체 흐름은 다음과 같다.

나쁜 schema
→ data redundancy
→ update anomaly
→ functional dependency 분석
→ relation decomposition
→ 1NF, 2NF, 3NF, BCNF 적용
→ 필요 시 denormalization 검토

정규형은 다음 한 줄로 요약할 수 있다.

1NF: atomic value
2NF: partial dependency 제거
3NF: transitive dependency 제거
BCNF: 모든 determinant는 candidate key

정규화 문제를 풀 때는 다음 질문을 순서대로 적용하면 된다.

  1. 각 attribute는 atomic value인가?
  2. 기본 키는 무엇인가?
  3. key가 아닌 attribute가 composite key 전체에 의존하는가?
  4. key가 아닌 attribute가 다른 key가 아닌 attribute를 결정하는가?
  5. 모든 determinant가 candidate key인가?
  6. 분해 후 join하면 원래 relation을 정확히 복원할 수 있는가?
  7. 성능상 join 비용이 크다면 denormalization이 필요한가?

정규화의 핵심은 정의 암기가 아니라 attribute가 무엇에 의해 결정되는지를 추적하는 것이다. FD를 정확히 잡으면 1NF, 2NF, 3NF, BCNF의 판단 기준도 자연스럽게 연결된다.