[DB] 제1,2,3 정규화와 반정규화

Anomaly, Join, Trade-off

Posted by Sol on April 09, 2021 · 8 mins read

이상현상(Anomaly)을 바로잡는 정규화(Normalization)

정규화를 한 마디로 정의하면 중복 제거 전략이다.

다량의 데이터를 2차원 행렬에 쑤셔 넣으면 중복이 발생할 소지가 다분한데,

중복을 제거해야 하는 이유는 데이터의 이상현상(Anomaly) 때문이다.

데이터들이 불필요하게 중복되면 내가 원하는 데이터만 조작할 수 없고,

중복된 다른 데이터들까지 영향을 받게 되므로 당연히 이상현상이 발생할 수밖에 없다.

정규화는 총 6단계까지 있다고 하는데..

주로 3NF까지 정규화 작업이 이루어진다고 하므로 본 포스팅에서는 1NF, 2NF, 3NF를 살펴보려 한다.


이상현상

아래와 같은 직원정보 테이블이 있다고 해보자.

ID Name Age Department Club (동호회)
1 Jack 25 Dev Basketball
2 Son 44 Sales Soccer
2 Son 44 Sales Baseball
3 Becky 32 Marketting Soccer
3 Becky 32 Marketting Basketball

딱 봐도 뭔가 중복된 것들이 많아보인다…

위 테이블을 가지고 대표 이상현상 세 가지를 살펴보도록 하자.


1) Insertion Error (삽입 이상)

삽입이상은 내가 데이터를 추가하고 싶을 때,

다른 컬럼의 조건을 만족하지 못해 불필요한 값을 넣어야 하는 현상이다.

가령 위 Table에서, 신입사원이 들어왔는데 아직 팀에 배치되지 않은 경우를 생각해보자.

만약 Department가 Not Null 속성을 지닌다면,

해당 신입사원 튜플은 Department 컬럼에 불필요한 값이라도 넣어야 삽입이 가능해진다.


2) Update Error (갱신 이상)

만약 Marketting 팀 명칭이 Data Marketting 이라는 팀으로 변경되어,

컬럼값을 Update해야하는 상황이 발생했다고 해보자.

그리고 첫 번째 Becky 튜플은 잘 수정했는데 그 다음 튜플은 깜빡하고 수정하지 않았다면?

뭐가 현재 올바른 컬럼값인지 알지 못할 것이다.

즉, 모든 튜플을 일일히 찾아서 수정해야 하는 번거로움이 발생한다.


3) Deletion Error (삭제 이상)

삭제 이상은 내가 의도하지 않은 데이터까지 함께 잃어버리는 현상이다.

1번 직원 Jack이 Basketball 동호회를 탈퇴한다고 생각해보자.

그럼 Jack 튜플에서 Basketball 데이터는 삭제되어야 하는데…

현재 구조로서는 Basketball 데이터를 삭제하기 위해서는 1번 튜플 전체를 날려야 한다.

(그리고 Jack 데이터는 테이블에서 소실된다)

즉, 삭제를 원하지 않는 데이터들까지 한꺼번에 삭제되는 현상이다.

(이 현상을 해결하기 위해서는 위 table을 분리해야 할 것이다)


위와 같은 이상현상을 바로잡는 것이 정규화의 역할이다.


제1정규화(First Normal Form - 1NF)

가장 기본적인 중복제거 전략으로, 도메인 원자값을 보장하도록 만드는 것이다.

도메인 원자값이라 하니 말이 어려워보이는데,

그냥 한 도메인 공간에 하나의 데이터만 있어야 한다는 뜻이다.

ID Name CellPhone
1 Jack 010-1234-4566 ,
010-4983-1019

위와 같이 전화번호 속성에 담긴 값이 2개라면 중복이 발생하므로,

ID Name CellPhone
1 Jack 010-1234-4566
2 Jack 010-4983-1019

이렇게 데이터를 2개로 쪼개어주는 것이 제1정규화이다. Very easy


제2정규화(Second Normal Form - 2NF)

제 2정규화는 설명이 조금 복잡한데….위키피디아에는 다음과 같이 정의되어있다.

a relation is in 2NF if it is in 1NF and every non-prime attribute of the relation is dependent on the whole of every candidate key.

라고 되어있다. 즉,

“1NF를 만족하면서, PK가 아닌 속성들이 전체 후보키에 의존적인 관계”를 의미한다.

이게 무슨 말인지 수학을 끌어들여서 설명해보자.

y = f(x) 라는 함수에서, y값은 x값에 따라 결정된다.

따라서 x는 결정자, y는 종속자라고 한다.

테이블에서 PK가 될 수 있는 attribute들을 후보키라고 하는데,

(후보키가 딱 하나일 수도 있고, 그 경우 후보키 = 기본키가 된다)

후보키는 결정자, 후보키가 아닌 attribute는 결정자로 볼 수 있을 것이다.

만약 특정 attribute가 모든 후보키에 종속된다면, 그 관계는 완전 함수 종속성을 지닌다.

반면, attribute가 일부 후보키에만 종속된다면, 그 관계는 부분 함수 종속성을 지닌다.

다시 정의로 돌아가보면,

테이블의 모든 속성이 후보키와 완전 함수 종속성을 지니는 관계일 때

그것을 Second Normal Form(2NF) 이라고 할 수 있다.

따라서 제2정규화는 복합키일때만 신경써주면 된다.

아래 테이블을 보자.

ID Name Age Club (동호회) Coach Position
1 Jack 25 Basketball James Guard
2 Son 44 Soccer Messi DF
2 Son 44 Baseball Park Catcher
3 Becky 32 Soccer Messi FW
3 Becky 32 Basketball James Center

위 테이블의 PK는 복합키로 ID, Club 이다.

각 컬럼의 종속성을 살펴보면,

  • Name, Age는 ID에 종속되나, Club에는 종속되지 않아 부분적 함수 종속성을 가짐.
  • Coach는 Club에 종속되나 ID에는 종속되지 않아 부분적 함수 종속성을 가짐.
  • Position은 ID, Club 에 모두 종속되어 완전 함수 종속성을 가짐.

부분적 함수 종속성이 존재하므로, 위 테이블은 2NF가 되지 않은 경우이다.

따라서 위 테이블은 앞서 언급한 Anomaly가 발생할 수 있다.

  • Club에 가입하지 않은 사람을 row로 넣을 때 난감함(삽입 이상)
  • Club 이름이 바뀌어서 Update를 할 때 빼먹을 수 있음(갱신 이상)
  • Jack이 Club을 탈퇴할 경우 Jack의 전체 튜플이 몽땅 날라감(삭제 이상)

이 경우 테이블을 분리함으로서 2NF를 완성시킬 수 있다.

ID name age
1 Jack 25
2 Son 44
3 Becky 32


Club Coach
Basketball James
Baseball Park
Soccer Messi


ID Club Position
1 Basketball Guard
2 Soccer DF
2 Baseball Catcher
3 Soccer FW
3 Basketball Center

위와 같이 테이블을 나눌 경우,

각 테이블의 PK가 아닌 attributes는 오직 완전 함수 종속성만을 갖게되므로,

제2정규화가 수행되었다고 할 수 있다.


제3정규화

2NF는 복합키 일때만 신경써주면 된다고 했었지만,

테이블이 복합키가 아님에도 중복현상이 발생할 수 있다.

이 경우 마찬가지로 테이블 분리를 해주어야 하며, 이를 제3정규화라 한다.

2NF와 마찬가지로 수학적으로 설명하면,

기본키가 아닌 모든 attribute가 기본키에 대해 ‘이행적 함수 종속’이 이루어지는 경우를 3NF라 한다.

이행적 함수 종속이란,

A -> B / B -> C 이면 A -> C 인 것을 만족함을 뜻한다.

ID Name Age Club (동호회) Coach Position
1 Jack 25 Basketball James Guard
2 Son 44 Soccer Messi DF
3 Becky 32 Basketball James Center

위 테이블을 보자.

기본키는 ID 하나로 구성되어 있으므로 2NF를 만족한다고 볼 수 있다.

그럼에도 불구하고 Club, Coach 컬럼에서 중복 현상이 발견되는데,

이는 Name, Age는 ID와의 이행적 함수 종속성이 있으나,

Club, Coach는 ID와의 이행적 함수 종속성이 없기 때문이다.

클럽 종류나 감독 이름은 사실 학생 ID와는 전혀 관계가 없다.

(Coach 컬럼은 오히려 Club 컬럼에 이행적 함수 종속이 있다)

이처럼 복합키가 아닌데도 기본키에 대해 이행적 함수 종속성이 이루어지지 않는 경우,

3NF를 수행하여야 하며 그 결과는 아래와 같다.

ID name age
1 Jack 25
2 Son 44
3 Becky 32


Club Coach
Basketball James
Baseball Park

아래 테이블의 경우 기본키가 없으므로 Club 컬럼을 기본키로 지정해주거나,

Sequence, id 같은 새로운 컬럼을 만들어 기본키로 지정하여 분리하면 된다.


반정규화

정규화는 Anormaly를 방지하기 위해 수행한다는 것을 살펴보았다.

그런데 정규화는 무조건 좋기만 한걸까?

여기서도 CS의 기본 원리인 Trade-off가 있다.

데이터를 조회할 때는 항상 중복되지 않는 데이터만 조회하는 것이 아니다.

여러 테이블을 JOIN하여 조회하는 경우가 비일비재하다.

JOIN은 기본적으로 교집합을 가진 테이블의 데이터를 이리저리 조회하겠다는 것인데…

그렇기에 JOIN은 의도적으로 중복된 데이터를 가져오는 의미를 갖는다.

JOIN을 많이 수행하면 기본적으로 성능이 저하된다.

따라서 다수의 JOIN이 필요한 특정 상황에서는 정규화를 하지 않는 것이 더 이득일 수도 있다.

데이터 무결성 vs 조회 성능 사이의 Trade-off 인 것이다.

이처럼 데이터 무결성의 Risk를 무릅쓰고 의도적으로 중복된 데이터를 만드는 것이 반정규화다.

위 2NF에서도 2NF가 수행되지 않은 테이블은 2NF가 수행된 테이블을

id컬럼으로 JOIN한 결과이다.

만약 실무에서 저 2NF가 되지 않은 테이블을 계속 조회하게 된다면,

해당 테이블을 비정규화하는 것이 성능 면에서 나을 것이다.