관계형 데이터베이스를 배우거나 SQLD (SQL 개발자), 정보처리기사 등 시험에 자주 출제되지만, 처음 배우면 함수종속성, 중복속성, 부분종속, 이행종속 등등.. 한자어로 1~2줄 설명으로 끝나기 십상이라 이해하기 힘들었던 경험 모두 있을 것이다. 쉽게 이해해보자.
1. 데이터베이스 정규화 (Normalization)
관계형 데이터베이스 설계에서 중복을 최소화하도록 데이터를 구조화하는 것을 정규화(Normalization) 라고 한다.
2. 제1정규화, 제1정규형(1NF)
※ NF는 Nomal Format의 약자이다
우리는 파스타 면의 판매업체로 다음과 같은 상품판매채널 테이블이 존재한다고 가정하자.
상품모델(id) | 상품명(item_name) | 판매채널(channel) |
s001 | 스파게티 | 자사몰 |
p001 | 페투치네 | 오픈마켓 |
l001 | 링귀니 | 자사몰 |
자사몰에서만 판매하고있던 링귀니를 대형마트에 납품하게 되었다고 가정해보자. 그렇다면 어떻게 추가해야할까?
이렇게 기존 링귀니 판매채널 컬럼에 '대형마트'를 추가하는 방법이 있다.
상품모델(id) | 상품명(item_name) | 판매채널(channel) |
s001 | 스파게티 | 자사몰 |
p001 | 페투치네 | 오픈마켓 |
l001 | 링귀니 | 자사몰, 대형마트 |
그런데 이렇게하면 나중에 문제가 될 수 있다.
만약 자사몰에서만 판매하는 상품만 찾고싶다면? 조건문에 신경을 써야한다.
-- 아래와 같이 검색하면 판매채널에 '자사몰'만 등록되어있는 데이터만 찾아진다.
SELECT * FROM item WHERE channel = '자사몰';
-- 따라서 다음과 같이 조건식을 사용해야 할 것이다.
SELECT * FROM item WHERE channel LIKE '%자사몰%';
그리고 또, 갑자기 판매채널 명칭을 바꾼다고 한다면? '대형마트'에서 세분화하여 '이마트'로 변경한다면? 데이터 수정이 쉽지 않다.
그래서, 보통은 다음과 같은 방법으로 데이터를 추가한다.
상품모델(id) | 상품명(item_name) | 판매채널(channel) |
s001 | 스파게티 | 자사몰 |
p001 | 페투치네 | 오픈마켓 |
l001 | 링귀니 | 자사몰 |
l001 | 링귀니 | 대형마트 |
이렇게 하나의 컬럼에는 하나의 데이터만 포함하는 것을 제1정규화 라고 한다.
그리고 이러한 제1정규화 된 테이블을 제1정규형(1NF)을 만족하는 테이블 이라고 한다.
3. 제2정규화, 제2정규형(2NF)
다음과 같이 상품판매채널 테이블에 데이터가 있다고 가정하자.
상품모델(id) | 상품명(item_name) | 가격(price) | 판매채널(channel) |
s001 | 스파게티 | 3000 | 자사몰 |
p001 | 페투치네 | 3500 | 오픈마켓 |
l001 | 링귀니 | 5000 | 오픈마켓 |
l001 | 링귀니 | 5000 | 자사몰 |
l001 | 링귀니 | 5000 | 대형마트 |
만약에 링귀니 제품의 가격 상승이 발생하면 어떻게될까? 지금은 고작 5개의 데이터 뿐이지만..
실제로 수많은 데이터들은 존재할텐데, 그 많은 데이터의 가격을 일괄 수정해야한다.
그러면 시간과 비용이 많이 발생하게 되버린다.
이러한 비효율을 방지하기위해 하는 것이 제2정규화라 생각하면 된다.
현재 테이블과 관련없는 데이터를 테이블로 따로 빼서 관리하는 것을 의미한다.
잘 생각해보자. 현재 테이블에서 가격(price) 컬럼은 반드시 존재해야 하는 컬럼일까?
다음과 같이 상품가격 테이블을 따로 만들어서 가격만 따로 관리하면 되지 않을까?
- 상품판매채널 테이블
상품모델(id) | 상품명(item_name) | 판매채널(channel) |
s001 | 스파게티 | 자사몰 |
p001 | 페투치네 | 오픈마켓 |
l001 | 링귀니 | 오픈마켓 |
l001 | 링귀니 | 자사몰 |
l001 | 링귀니 | 대형마트 |
- 상품가격 테이블
상품모델(id) | 상품명(item_name) | 가격(price) |
s001 | 스파게티 | 3000 |
p001 | 페투치네 | 3500 |
l001 | 링귀니 | 5000 |
이를 명확히 정의하면 테이블 내에서 부분 함수 종속(Partial Functional Dependency)을 제거하여 완전 종속인 테이블을 만드는 것이 제2정규화 라고 하며, 제2정규화 된 테이블을 제2정규형(2NF) 테이블이라 한다.
이렇게 제2정규화를 하면, 상품가격 데이터 수정을 할 때 전체를 수정하지않고 한곳만 수정하면 되므로 성능상 우수해진다.
하지만, 상품판매채널 테이블만으로 상품가격을 알 수 없게 되므로, 상품가격을 알기 위해서는 JOIN 등의 작업이 필요해진다.
■ 부분 함수 종속(Partial Functional Dependency)에 대해서
다음과 같이 상품판매채널 테이블을 보자. 가격은 '판매채널'과 관계까 있을까? '판매채널'과 관계가 있다면, 판매채널마다 가격이 정해질 것이다. 하지만 여기서 가격은 상품이 무엇이냐에 따라 정해지는 것이기 때문에 가격(price) 은 상품모델(id)과 상품명(item_name) 에 종속된다. 이럴때 상품모델(id)과 상품명(item_name)이 결합하여 하나의 Key가 되고 가격은 종속되고 있다. 이런 것을 두고 테이블에 부분종속적인 컬럼이 있다고 표현한다.
※ 두 개 이상의 컬럼을 key 로 지정하는 것을 복합 키 (Composite Key) 라고한다.
상품모델(id) | 상품명(item_name) | 가격(price) | 판매채널(channel) |
s001 | 스파게티 | 3000 | 자사몰 |
p001 | 페투치네 | 3500 | 오픈마켓 |
l001 | 링귀니 | 5000 | 오픈마켓 |
l001 | 링귀니 | 5000 | 자사몰 |
l001 | 링귀니 | 5000 | 대형마트 |
4. 제3정규화, 제3정규형(3NF)
다음과 같은 테이블이 있다고 가정하자.
- 상품가격 테이블
- PK : 상품모델(id), 상품명(item_name)
상품모델(id) | 상품명(item_name) | 가격(price) | 공급자(vendor) | 공급국가(factory_country) |
s001 | 스파게티 | 3000 | DECE | italy |
p001 | 페투치네 | 3500 | RUMM | italy |
l001 | 링귀니 | 5000 | VALL | america |
공급자의 국가(factory_country)는 공급자(vendor)에 종속적이다.
여기서 공급자(vendor)는 일반 컬럼이고 키역할을 하지 않는다.
공급자의 국가(factory_country)는 PK와 아무 연관이 없다.
일반 컬럼에만 종속되는 데이터를 분리하는 것을 제3정규화 라고 한다.
제3정규화 된 테이블을 제3정규형(3NF) 테이블 이라 한다.
- 상품가격 테이블
- PK : 상품모델(id), 상품명(item_name)
상품모델(id) | 상품명(item_name) | 가격(price) | 공급자(vendor) |
s001 | 스파게티 | 3000 | DECE |
p001 | 페투치네 | 3500 | RUMM |
l001 | 링귀니 | 5000 | VALL |
- 공급자 테이블
- PK : 공급자(vendor)
공급자(vendor) | 공급국가(factory_country) |
DECE | italy |
RUMM | italy |
VALL | america |
이렇게 나눠두면 수정등의 작업에 용이해진다.
하지만 상품명으로 공급국가를 알기 위해서는 테이블 하나만으로 알 수 없고 JOIN 의 작업이 요구된다.
5. 반정규화(De-normalization)
정규화된 데이터를 시스템의 성능향상, 단순화 등을 목적으로 중복, 통합, 분리 등을 수행하는 기법이다.
정규화로 인하여 테이블이 분리되었을때, 과도한 JOIN 이 발생하여 조회성능저하가 예상되는 경우 수행하므로 다음과 같은 장점이 있다.
- JOIN 을 제거하는 것이기 때문에 검색시간이 더 빨라진다.
- JOIN 의 기술적 문제가 해결될 수 있다.
중복, 통합 등이 발생할 수 있으므로 다음과 같은 단점이 발생할 수 있다.
- 데이터의 삽입, 수정, 삭제 등의 비용이 높아진다.
- 중복, 통합은 많은 저장 공간이 요구된다.