데이터 베이스

Ch.9 정규화 [데이터베이스 개론]

ImKDM 2023. 5. 13. 19:37
728x90

정규화의 개념과 이상 현상


정규화의 개념


데이터베이스를 설계한 후 설계 결과물을 검증하기 위해 사용한다.

 

데이터베이스를 잘못 설계하면 불필요한 데이터 중복이 발생하여 릴레이션에 대한 데이터의 삽입, 수정, 삭제 연산시 부작용이 발생할 수 있다. 이러한 부작용을 이상 현상이라고 하는데, 이상 현상을 제거하면서 데이터베이스를 설계해나가는 과정을 정규화라고 한다.

이상 현상의 종류


3가지 이상 현상이 존재한다.

 

삽입 이상  :  새 데이터를 삽입하기 위해 불필요한 데이터도 함께 삽입해야 하는 문제

갱신 이상  :  중복 튜플 중 일부만 변경하여 데이터가 불일치하게 되는 모순의 문제

삭제 이상  :  튜플을 삭제하면 꼭 필요한 데이터까지 함께 삭제되는 데이터 손실의 문제

 

 

예시로 잘못 설계된 릴레이션을 참고해보자. 아례 사례에서 한 고객은 다양한 이벤트를 응모할 수 있다. 따라서 고객아이디만으로 고객을 유일하게 식별할 수 없어 이벤트번호 속성을 함께 사용해서 기본키로 구성한다. 

 

이벤트 참여 릴레이션

 

위 릴레이션엔 고객에 대한 정보인 고객아이디, 고객이름, 등급과 참여한 이벤트에 대한 정보인 이벤트번호, 당첨여부를 한 번에 전부 포함하고 있다. 보기에는 모든 정보를 한 눈에 파악할 수 있으니 좋은 릴레이션 아닐까 싶다. 하지만 그렇지 않다.

 

일단 고객 한 명이 여러 이벤트에 참여할 수 있으므로 동일한 고객의 이름과 등급이 여러 번 나타날 수 있다. 예를 들어 apple 고객은 3개의 이벤트에 참여하므로 고객이름과 등급이 무려 세 번이나 저장된다. 동일한 데이터가 여러 번 중복되어 저장되면 저장 공간 낭비와 더불어 릴레이션에 삽입, 수정, 삭제할 때 이상 현상이 발생할 수 있다.

삽입 이상 (insertion anomaly)

릴레이션에 새 데이터를 삽입하기 위해 원치 않는 불필요한 데이터도 함께 삽입해야 하는 문제

 

만약 이벤트에 참여하지 않은 'core'라는 신규 회원을 릴레이션에 등록해보자. 그러나 이벤트번호 속성을 기본키로 포함하고 있어서 이벤트에 참여하지 않았기 때문에 기본키를 널 값 가질 수밖에 없기 때문이다. 그런데 기본키는 널 값을 가질 수 없지 않은가. 그래서 이벤트번호 속성에 실제로 참여하지 않은 '임시' 이벤트번호를 삽입해야 한다. 따라서 해당 릴레이션에는 삽입 이상이 발생하게 된다.

 

갱신 이상 (update anomaly)

릴레이션의 중복된 튜플들 중 일부만 수정하여 데이터가 불일치하게 되는 모순이 발생하는 문제

 

현재 apple 고객의 고객이름, 등급이 중복해서 존재한다. 만약 apple 고객의 등급이 vip로 상향된다면 apple 고객의 모든 등급을 gold에서 vip로 변경해야 하는 수고로움이 발생한다. 또한 실수로 일부 튜플의 등급만 수정된다면 동일한 고객의 등급이 서로 다른 모순이 생겨 갱신 이상이 발생하게 된다. 

 

삭제 이상 (deletion anomaly)

릴레이션에서 튜플을 삭제하면 꼭 필요한 데이터까지 함께 삭제하여 데이터가 손실되는 연쇄 삭제 현상

 

만약 단 하나만 존재하는 튜플의 일부 속성을 삭제한다고 했을 때 다른 모든 데이터가 함께 소멸되는 현상을 일컸는다.

예를 들어, 위 릴레이션에서 orange 고객이 이벤트를 취소하여 테이블에서 관련 튜플을 삭제해야 한다면 orange 고객이 참여하고 있는 이벤트에 대한 정보만 가지고 있는 것이 아니라, 해당 고객에 대한 정보들 모두(고객아이디, 고객이름, 등급)에 대한 정보도 사라지게 된다. 왜냐하면 해당 튜플이 이벤트 관련 정보와 회원 관련 정보, 모두를 동시에 가지고 있기 때문이다. 즉, 이 튜플을 삭제하면 이벤트참여와 관련없는 고객 정보들까지도 원치 않게 손실되는 삭제 이상이 발생하게 된다.

 

정규화의 필요성


위에서 본 것처럼 여러 이상 현상이 발생하는 이유는 무엇일까?

 

관련이 없는 데이터, 속성들을 하나의 릴레이션에 모아두고 있기 때문이다

 

이상 현상이 발생하지 않도록 하려면, 관련 있는 속성들로만 릴레이션을 구성해야 하는데 이를 위해 필요한 것이 정규화이다. 정규화란, 이상 현상을 방지하고자 관련있는 속성들로만 구성하기 위해 릴레이션을 분해(decomposition)하는과정이다.

 

정규화를 수행하려면 먼저 릴레이션을 구성하는 속성들 간의 관련성을 판단할 수 있어야 한다. 정규화 과정에서 고려해야 하는 속성들 간의 관련성을 함수적 종속성(FD : Functional Dependency)라고 한다.

 

일반적으로 릴레이션에 하나의 함수적 종속성이 존재하도록 정규화를 통해 릴레이션을 분해한다. 

함수 종속


하나의 릴레이션을 구성하는 속성들의 부분 집합을 X와 Y라 할 때, 어느 시점에서든 릴레이션 내의 모든 튜플에서 X 값에 대한 Y 값이 항상 하나면 "X가 Y를 함수적으로 결정한다" 또는 "Y가 X에 함수적으로 종속되어 있다"라고 한다.

 

함수적 종석 관계는 X -> Y로 표현하고, X를 결정자, Y를 종속자라고 한다.

 

 

예시로 고객 릴레이션을 보자.

 

고객 릴레이션

 

고객 릴레이션에서 각 고객아이디 속성 값에 대응되는 고객이름 속성과 등급 속성의 값이 단 하나이므로, 고객아이디가 고객이름과 등급을 결정한다고 볼 수 있다. 고객아이디가 apple인 고객은 이름이 정소화, 등급은 gold인 한 사람밖에 없다,. 따라서 고객 릴레이션에서 고객이름과 등급속성은 고객아이디 속성에 함수적으로 종속되어 있다.

 

 

함수 종속 관계를 판단할 때 유의할 점이 있다. 바로 현재 시점에 릴레이션에 포함된 속성 값만으로 판단하면 안 된다는 것이다. 속성 값은 계속 변할 수 있다. 따라서 속성 값이 아닌 속성 자체가 가지고 있는 특성과 의미로 판단해야 한다.

 

고객 릴레이션에서 고객아이디는 고객을 구별해주는 기본키 속성이기 때문에 아이디가 같은 서로 다른 고객이 존재할 수 없다. 그러므로 고객아이디가 정해지면 오직 하나의 고객이름과 등급이 결정된다.

 

일반적으로 튜플을 유일하게 구별하는 기본키와 후보키는 그 특성 때문에 릴레이션을 구성하는 다른 모든 속성들을 함수적으로 결정한다. 물론 기본키나 후보키만 결정자가 될 수 있는 것은 아니다. 기본키, 후보키가 아니더라도 속성 Y 값을 유일하게 결정하는 속성 X는 함수 종속 관계에서 모두 결정자가 될 수 있다. 

 

 

예시로 이벤트참여 릴레이션에서 [고객아이디]는 [고객이름]을 유일하게 결정한다. 고객아이디가 같으면 모든 튜플에서 고객이름이 반드시 같기 때문이다. 그래서 고객이름은 종속자가 고객아이디는 결정자가 된다.

 

그리고 [고객아이디, 이벤트번호] 속성 집합은 [당첨여부] 속성을 유일하게 정한다. apple 고객이 참여한 E001 이벤트의 당첨 여부는 Y만 존재하기 때문이다. 따라서 당첨여부는 [고객아이디, 이벤트번호]에 종속되어 있어 [고객아이디, 이벤트번호]는 결정자, 당첨여부는 종속자가 된다. 물론 [고객이름]도 기본키인 [고객아이디, 이벤트번호]에 종속되어 있다.

 

 

[당첨여부] 속성은 [고객아이디, 이벤트번호]란 기본키 속성 집합 전체에 종속되어 있다. 이런 경우 당첨여부 속성이 [곡객아이디, 이벤트번호] 속성 집합에 완전 함수 종속(FFD : Full Functional Dependency)되어있다고 한다. 완전 함수 종속은 릴레이션에서 속성 집합 Y가 속성 집합 X 전체에 종속된 것을 의미한다. 

 

반대로 부분 함수 종속(PFD : Partial Functional Dependency)은 속성 집합 Y가 속성 집합 X의 전체가 아닌 일부분에도 함수적으로 종속됨을 의미한다. 따라서 부분 함수 종속 관계가 성립하려면 결정자가 여러 개의 속성들로 구성되어 있어야 한다.

 

 

위 관계를 봤을 때 [당첨여부] 속성은 [고객아이디, 이벤트번호]에 완전 함수 종속되어 있다. 하지만 [고객이름] 속성은 [고객아이디]로만 식별이 가능하기 때문에 부분 함수 종속되어 있다고 볼 수 있다.

기본 정규형과 정규형 과정


정규화의 개념과 정규화의 종류


정규화(normalization)은 릴레이션을 연관성이 있는 속성들로만 구성되도록 분해해서, 이상 현상이 발생하지 않는 릴레이션으로 만들어가는 과정을 의미한다. 그래서 정규화의 기본 목표는 관련이 없는 함수 종속을 별개의 릴레이션으로 표현하는 것이다.

 

릴레이션이 정규화된 정도는 정규화(NF : Normal Form)으로 표현한다. 

 

 

일반적으로 차수가 높은 정규형에 속하는 릴레이션일수록 데이터 중복이 줄어 데이터 중복에 의한 이상 현상이 발생하지 않는다. 하지만 모든 릴레이션이 제5정규형에 속해야 되는 건 아니다. 

 

 

제 1 정규형 (1NF : First Normal Form)


릴레이션에 속한 모든 속성의 도메인이 원자 값으로만 구성되어 있으면 제 1 정규형에 속한다

 

릴레이션에 속한 모든 속성이 더는 분해되지 않는 원자 값만 가져야 한다. 한마디로 속성이 다중 값을 가지면 안된다. 

 

 

만약 한 고객이 다수의 이벤트에 참여할 수 있다고 가정하자. 그러면 이벤트번호와 당첨여부에 다중 값을 가질 수 있다. 하지만 관계 데이터베이스의 릴레이션은 모든 속성이 원자 값을 가지는 특성이 있기 때문에 최소한 제 1 정규형을 만족하려면 튜플마다 [이벤트번호]와 [당첨여부] 값을 하나씩만 포함하도록 분해해야 한다.

 

 

자, 이제 각 속성마다 원자값을 가졌으므로 적어도 제 1정규형은 만족한다. 그러나 불필요한 데이터 중복이 발생한다. apple 고객의 등급이 gold이고, 할인율이 10%인 정보는 모든 apple 고객의 튜플에 중복되어 등장한다. 따라서 해당 릴레이션은 제 1정규형을 만족하지만, 이상 현상이 발생할 수 있기 때문에 바람직한 릴레이션이 아니다.

 

해당 릴레이션의 함수 종속 관계를 파악해보자. 

 

총 5개의 속성으로 구성되어 있고 [고객아이디, 이벤트번호] 속성 집합이 기본키 역할을 담당한다. [고객아이디]가 [등급]와 [할인율]을 유일하게 결정하며, [등급]에 따라 [할인율]이 결정된다. 그리고 [고객아이디, 이벤트번호]가 [당첨여부] 속성을 유일하게 결정한다.

 

 

 

등급과 할인율 속성의 값이 중복되어 나타나는 경우, 삽입/갱신/삭제 이상 현상이 발생할 수 있다.

삽입 이상

위 릴레이션의 기본키는 [고객아이디, 이벤트번호]라서 새 고객에 대한 데이터를 삽입하려면 그 고객은 이벤트에 무조건 참여해야 한다. 그렇지 않으면 이벤트번호에 널 값이 들어가 개체 무결성 제약조건을 위반하게 된다(기본키는 널 값을 가질 수 없다). 그래서 고객아이디와 등급, 할인율만 삽입하는 것이 불가능하다.

 

갱신 이상

고객 아이디가 apple인 고객의 튜플이 총 3개나 있다. 만약 이 고객의 등급이 gold에서 vip로 변경되면 새 튜플의 등급 속성의 값 모두 vip로 변경해야 한다. 실수로 일부 튜플의 등급 속성의 값만 변경하면 동일한 고객이 서로 다른 두 개의 등급 값을 가져서 데이터 일관성에 문제가 발생한다.

 

삭제 이상

고객 아이디가 orange 인 고객 관련 튜플은 단 한 개이다. 만약 이 고객이 참여한 E004 이벤트 참여 기록을 삭제하려면 이벤트와 관련없는 고객 정보, 즉 등급, 할인율도 모두 사라지게 된다. 고객 정보가 함께 삭제되므로 이 고객과 관련해 꼭 필요한 데이터를 유지할 수 없게 된다.

 

 

위와 같은 이상 현상이 발생한 이유가 뭘까? 바로 이 릴레이션이 부분 함수 종속을 포함하고 있기 때문이다. 기본키인 [고객아이디, 이벤트번호]에 완전 함수 종속되지 못하고 일부부인 고객아이디에 종속되는 등급과 할인율 속성이 문제가 된다. 기본키에 완전 함수 종속이 되지 못한 [등급], [할인율] 속성의 값이 릴레이션에서 여러 번 중복되어 나타나는 것, 그리고 관련이 없는 [이벤트번호], [당첨여부] 속성이 하나의 릴레이션에 존재하기 때문에 여러 이상 현상이 발생된다.

 

이를 해결하기 위해선 부분 함수 종속이 제거되도록 릴레이션을 분해해야 한다.

 

제 2 정규형 (2NF : Second Normal Form)


릴레이션이 제1 정규형에 속하고, 기본키가 아닌 모든 속성이 기본키에 완전 함수 종속되면 제2 정규형에 속한다

 

제1 정규형에 속하는 릴레이션 중 부분 함수 종속을 제거하여 모든 속성이 기본키에 완전 함수 종속되면 제2 정규형을 만족한다.

 

위 예시에서 봤듯이 [등급], [할인율] 속성이 관련 없는 [이벤트번호], [당첨여부] 속성과 같이 존재하며, 부분 함수 종속이 있었기 때문에 여러 이상 현상이 발생했다. 따라서 2개의 릴레이션으로 분해하면, 분해된 고객 릴레이션과 이벤트참여 릴레이션은 모두 제2 정규형에 속하게 된다.

 

 

정규화 과정에서 릴레이션을 분해할 땐, 분해된 릴레이션들을 자연 조인하여 분해 전의 릴레이션으로 다시 복원할 수 있어야 한다. 이 말은 즉, 릴레이션이 의미상 동등한 릴레이션들로 분해되어야 하고, 릴레이션을 분해했을 때 정보 손실이 발생하지 않아야 한다. 이처럼 정보의 손실 없이 릴레이션을 분해하는 것을 무손실 분해라고 한다.

 

 

하지만 분해된 고객 릴레이션에선 또 다시 이상 현상이 발생할 수 있다. 제2 정규화로 부분 함수 종속은 사라졌지만, 함수 종속성을 아직도 여러 개 포함하고 있기 때문이다.

삽입 이상

새로운 등급과 할인율이 테이블에 추가될 수 있다. 하지만 기본키가 [고객아이디]이기 때문에 새로 만들어진 등급에 속하는 고객이 있어야 릴레이션에 삽입할 수 있다. 등급이 생겼지만 아직 해당 등급에 속하는 고객이 없으면 고객 릴레이션에 삽입할 수 없다는 문제가 발생한다. 개체 무결성 제약조건 위반 때문이다.

 

갱신 이상

등급에 대한 할인율이 변경되면 해당 등급에 대한 모든 튜플에서 할인율 속성 값을 똑같이 변경해야 한다. 그렇지 않으면 같은 등급에 대해 할인율이 여러 개 존재하는 모순이 발생한다.

 

삭제 이상

고객 탈퇴로 고객 릴레이션에서 튜플이 삭제되면 등급과 할인율에 대한 정보까지 삭제된다. 이는 고객에 대한 튜플을 삭제할 시 등급에 대한 정보도 함께 삭제되기에 문제가 발생한다.

 

 

이러한 여러 이상 현상이 발생하는 이유는 함수 종속 관계를 여러 개 포함하고 있어 이행적 함수 종속이 생기기 때문이다. 이를 해결하기 위해 제3 정규형을 실시한다.

제 3 정규형 (3NF : Third Normal Form)


릴레이션이 제2 정규형에 속하고, 기본키가 아닌 모든 속성이 기본키에 이행적 함수 종속이 되지 않으면 제3 정규형에 속한다

 

이행적 함수 종속(transitive FD)이란, 릴레이션을 구성하는 3개의 속성 집합 X, Y, Z에 대해 함수 종속 관계 X -> Y와 Y -> Z가 존재하면 논리적으로 X -> Z가 성립한다. 이때 속성 집합 Z가 속성 집합 X에 이행적으로 함수 종속되었다고 한다.

 

 

모든 속성이 기본키에 이행적 함수 종속이 되지 않도록 릴레이션을 분해하는 정규화 과정을 거쳐야 제3 정규형을 만족할 수 있다.

 

기본키가 [고객아이디]이므로 [등급]과 [할인율] 속성이 고객아이디에 함수적으로 종속된다.

 

그런데 고객아이디가 등급을 결정하고, 등급이 할인율을 결정하는 관계를 나타낸다. 고객아이디가 등급을, 등급이 할인율을 결정한다. 즉, 할인율이 기본키인 고객아이디에 함수적으로 종속됨과 동시에 등급을 통해서 고객아이디에 이행적으로 종속되는 것이다.

 

이러한 이행적 함수 종석이 일어나는 이유는 여러 함수 종속 관계가 하나의 릴레이션에 존재하기 때문이다. 사실 할인율은 등급에 따라 달라지지, 고객아이디에 따라 할인율이 달라지는 것이 아니다. 그런데 [고객아이디, 등급, 할인율] 속성이 모두 하나의 릴레이션에 모아놓다 보니, 고객아이디가 할인율을 결정하게 되서 이상 현상이 발생하게 된 것이다. 

 

이러한 이상 현상을 막기 위해 이행적 함수 종속이 나타나지 않게 2개의 릴레이션으로 분해해야 한다.

 

위 릴레이션은 고객아이디 -> 등급,  등급 -> 할인율의 함수 종속 관계를 유지할 수 있도록 2개의 릴레이션으로 분해하면 된다. 새로 분해된 고객 릴레이션과 고객등급 릴레이션으로 모두 제3 정규형에 속하게 된다.

 

보이스 / 코드 정규형 (BCNF :  Normal Form)


릴레이션의 함수 종속 관계에서 모든 결정자가 후보키이면 보이스/코드 정규형에 속한다

 

하나의 릴레이션에 여러 개의 후보키가 존재할 수 있다. 이 경우에는 제3 정규형을 만족하더라도 이상 현상이 발생할 수 있다. 후보키를 여러 개 가지고 있는 릴레이션에 발생할 수 있는 이상 현상을 해결하기 위해 좀 더 엄격한 제약조건을 제시한 것이 보이스/코드 정규형이다.

 

따라서 보이스/코드 정규형을 강한 제3 정규형(strong 3NF)라고도 한다. 보이스/코드 정규형이 제3 정규형보다 더 강력한 제약조건을 가지고 있다고 볼 수 있다.

 

제 3정규형에는 속하지만 보이스/코드 정규형에는 속하지 않는, 후보키가 여러 개인 릴레이션의 예를 살펴보자.

 

 

강좌신청 릴레이션은 고객이 인터넷강좌를 신청하면 해당 강좌의 담당강사에 대한 데이터를 저장한다.

강좌신청 릴레이션에서는 한 고객이 여러 인터넷강좌를 신청할 수 있지만, 동일한 인터넷강좌를 여러 번 신청할 수 없다. 그리고 강사 한 명이 인터넷강좌를 하나만 담당할 수 있고, 하나의 인터넷강좌는 여러 강사가 담당할 수 있다.

 

그래서 튜플을 구별할 수 있는 후보키로는 [고객아이디, 인터넷강좌]와 [고객아이디, 담당강사번호]가 있고, 이중 기본키로는 [고객아이디, 인터넷강좌]를 선정했다. 

 

당연히 기본키인 [고객아이디, 인터넷강좌]가 담당강사번호 속성을 함수적으로 결정한다. 

그리고 강사 한 명이 인터넷강좌를 하나만 담당하기 때문에 담당강사번호가 인터넷강조를 함수적으로 결정한다. 

 

 

위 릴레이션은 모든 속성이 원자 값으로만 구성되어 있으므로 제1 정규형에 속한다. 또 기본키가 아닌 속성인 [담당강사번호]가 기본키에 완전 함수 종속되어 있고, 이행적 함수 종속도 포함하지 않으므로 제2, 제3 정규형에도 속한다.

 

하지만 [담당강사번호] 속성이 후보키가 아님에도 인터넷강좌 속성을 결정하고 있다. 이는 보이스/코드 정규형에 속하지 않는다.

 

코이스/코드 정규형에 속하지 않는 릴레이션은 삽입, 갱신, 삭제 이상 현상이 발생할 수 있다.

삽입 이상

만약 특정 강사가 강좌를 하나 맡게 됐는데, 이 강좌를 신청한 고객이 없다면 이 내용을 강좌신청 릴레이션에 삽입할 수 없다. 왜냐하면 고객아이디 속성이 기본키로 널 값을 가질 수 없기 때문이다.

 

갱신 이상

P004 강사가 담당하는 강좌명이 변경되면 P004 강사와 관련된 모든 튜플의 인터넷강좌 속성의 값이 변경되어야 한다. 만약 1개만 변경하면 강사 한 명이 인터넷 강좌를 하나만 담당해야 한다는 전제 조건에 모순된다.

 

삭제 이상

고객아이디가 banana인 고객이 인터넷강좌 신청을 취소해서 해당 고객에 대한 튜플을 삭제하면, P002 강사가 기초토익 강좌를 담당하고 있다는 정보도 함께 삭제된다. 그런데 이 튜플은 P002 강사에 대한 정보를 담고 있는 유일한 튜플이므로 삭제하면 해당 강사에 대한 정보가 더 이상 존재하지 않게 된다.

 

 

여러 이상 현상이 발생하는 이유는 후보키가 아니면서 함수 종속 관계에서 다른 속성을 결정하는 [담당강사번호] 속성이 존재하기 때문이다. 이를 막기 위해 모든 결정자가 후보키가 될 수 있도록 릴레이션을 두 개로 분해해야 한다.

 

 

후보키가 아닌 결정자가 존재하지 않게 릴레이션을 분해하면 보이스/코드 정규형에 속한다.

 

제 4 정규형과 제 5 정규형


제4 정규형은 다치 종속을 제거해야 만족할 수 있다.

제5 정규형은 조인 종석을 제거애햐 만족할 수 있다.

 

실제 데이터베이스를 설계할 때 모든 릴레이션이 제5 정규형에 속해야하는 것은 아니다. 오히려 비효율적이고 바람직하지 않은 경우가 많다. 

정규화 과정 정리