TIL 57 | MySQL - 정규화

CHAEIN·2021년 8월 30일
0

MySQL

목록 보기
7/8

데이터 베이스 이상 현상

데이터 베이스에서 삽입, 업데이트 삭제를 제대로 할 수 없게 되는 경우

삽입 이상

새로운 데이터를 자연스럽게 저장할 수 없는 경우를 의미한다. 예를 들어 로우를 추가하려고 했을 때 모든 컬럼의 값을 가지고 있지 않은 경우 일부 컬럼 값으로 null이 들어갈 수 밖에 없다. 이러한 상황을 삽입 이상이라고 한다.

업데이트 이상

데이터를 업데이트 했을 때, 정확성을 지키기 어려워지는 경우를 의미한다. 만약 주문 테이블에 있는 상품의 이름이 변경됐을 때 동일 상품이지만 일부 상품의 이름만 수정되었다면 같은 상품이 여러개의 이름을 갖게 된다. 이러한 상황을 업데이트 이상이라고 한다.

삭제 이상

원하는 데이터만 삭제할 수 없는 경우를 의미한다. 한 로우를 삭제할 때 그 로우의 일부 값만 삭제하고 일부값은 보존하고 싶을 경우 삭제 이상 상황에 놓이게 된다.

데이터 이상 현상에 직면하는 이유는 데이터 모델링을 제대로 하지 않았기 때문이다.

정규화

데이터 베이스의 테이블이 잘 만들어졌는지 평가하고, 잘 만들지 못한 테이블을 고쳐나가는 과정으로 해당 컬럼이 해당 테이블에 속하는 것이 적절한지 아닌지를 판단해 컬럼의 위치를 조정한다.

정규형은 문제가 있는 컬럼이 있는지를 판단하는 규칙으로 1NF, 2NF, 3NF 처럼 순서에 따라 규칙이 누적된다. 3NF는 1NF와 2NF 규칙을 모두 포함한다. 정규형은 위 3개 외에 더 다양하지만 전문가 레벨이 아닌 이상 가장 보편적으로 활용되는 단계는 3NF 까지이다. 정규화는 데이터 모델을 만들고 실제 데이터베이스를 구현하기 전에 적용하는 것이 좋다.

정규화의 장점

  • 데이터베이스에서 삽입, 업데이트, 삭제 이상을 없앨 수 있다.
  • 새로운 종류의 데이터를 추가할 때 테이블 구조 수정을 많이 하지 않아도 된다.
  • 데이터베이스 구조를 단순화해서 사용자가 더 쉽게 이해할 수 있다.

1NF(제 1 정규형)

테이블 안 모든 로우의 모든 컬럼 값들이 나눌 수 없는 단일 값이어야 한다.

idnamephone_number
1김코딩010-000-0000, 111-1111-1111
2박자바010-123-1234, 010-444-4444

위의 테이블에서 phone_number 컬럼에는 단일 값이 아닌 여러개의 전화번호가 들어있다. 이러한 경우를 제 1 정규형에 부합하지 않는다고 한다. 이렇게 컬럼을 구성하면 전화번호를 조회하거나 수정하고 싶을 때 복잡해진다. 이 경우 phone_number테이블을 따로 만들어 user.id로 연결해주는 것이 바람직하다.

만약 한 컬럼에 서로 다른 종류의 값을 여러개 저장하고 있을 때, 예를 들어 영어 이름의 경우 first, middle, lastname이 한 컬럼에 저장되어 있을 때에는 한 컬럼을 여러개로 분리해서 모델링하는 것이 바람직하다.

함수종속성

x의 값에 따라서 y의 값이 결정될 때, y는 x에 함수 종속성이 있다고 한다. x -> y

idnameage
1김코딩23
2박자바23
32박자바26

위의 테이블에서 id가 unique한 값이라고 할 때 name과 age는 id에 대해 함수 종속성이 있다.(id -> {name,age}) id 값으로 name과 age값을 알수 있기 때문이다. 그러나 name이나 age만으로 id 값을 알아낼 수 없다.

productsizeprice
반팔티20020000
원피스10040000

이 경우 size와 price는 product에 따라 결정된다. 이때 size와 price는 product에 대해 함수 종속성이 있다.

함수종속성은 꼭 하나의 컬럼에만 해당되지 않고 쌍에 대해서도 해당될 수 있다.

userproductscore
김코딩반팔티4

만약 user는 한 상품에 하나의 점수밖에 주지 못한다면 score는 user,product쌍에 함수 종속성이 있다. {user,product} -> score

이행성

productbrandbrand_country
반팔티나이키미국
숄더백구찌이탈리아

brand와 brand_country는 product에 의해 결정되기 때문에 product에 함수 종속성이 있다. product -> {brand, brand_country} 그러나 자세히 따져보면 brand는 product에 의해 결정되는 것이 맞지만 brand_country는 brand에 따라 결정되므로 brand_country는 brand에 함수 종속성이 있다. 이 관계를 표현하면 product -> brand -> brand_country가 된다. 이처럼 하나 이상의 속성을 건너서 함수 종속성이 있는 경우에 함수 종속성이 이행되었다고 해서 brand_country는 brand에 이행적 함수 종속성이 있다고 표현한다.

Candidate Key

하나의 로우를 특정 지을 수 있는 attribute들의 최소 집합

iduser_idproduct_idscoredescription
1544여름에 입기 무난
21135동생이 좋아해요
3383사이즈가 많이 작음

위 테이블에서 id는 cadidate Key가 될 수 있다. 고유한 id 값이 특정 로우를 가리키기 때문이다. 또한, 유저 한명이 한 상품에 하나의 리뷰만 쓸수 있다면 {user_id, product_id} 한 쌍도 candidate key가 될 수 있다. 이 쌍으로 리뷰 테이블의 로우를 특정 지을 수 있다.

{user_id, product_id, score} 로 이루어진 쌍도 하나의 로우를 특정지을 수 있지만 score는 로우를 특정하는데 반드시 필요한 속성이 아니기 때문에 최소 집합을 요구하는 candidate key 정의에 따라 candidate key가 될 수 없다.

candidate key에 포함되는 attribute는 prime attribute라고 한다. 위 예에서 prime attribute는 id, user_id, product_id 가 된다. non prime attribute는 candidate key에 포함되지 않는 나머지 속성들을 의미한다.

2NF

제 2정규형 조건

  • 제 1정규형의 조건 테이블 안 모든 값이 나눌 수 없는 단일 값이어야한다. 를 만족해야한다.
  • 테이블에 candidate key의 일부분에 대해서만 함수 종속성이 있는 non prime attribute가 없어야 한다.
iduser_idproduct_idagepricescorecomment
15423500005무난무난

위와 같은 리뷰 테이블에서 id, user_id, product_id는 prime attribute이고 나머지는 non prime attribute이다. 이때 non-prime attribute인 age는 user_id에 대한 함수 종속성이 있고, price는 product_id에 대한 함수 종속성이 있다.

이처럼 non prime attribute가 candidate key인 {user_id, product_id} 전체에 대한 함수 종속성이 있지않고 일부분에 대해서만 함수 종속성이 있는 경우 제 2정규형을 만족하지 못한다고 할 수 있다.

이 경우엔 함수 종속성에 따라 age는 user테이블로, price는 product테이블로 옮겨주면 문제가 해결된다.

3NF

제 3정규형 조건

  • 제 2 정규형 조건에 만족해야한다.
  • 테이블 안에 있는 모든 attribute들은 오직 primary key에 대해서만 함수 종속성이 있어야 한다. (= 테이블의 모든 attribute는 직접적으로 테이블 Entity에 대한 내용이어야만 한다. = 이행적 함수 종속성이 있어선 안된다.)
ideventevent_numwinnerage
1N빵 프로모션4김코딩29

위 테이블은 테이블 안 모든 값이 나눌 수 없는 단일 값이라는 점에서 제 1정규형에 부합한다. 위 테이블에서 candidate key는 id와 {event, event_num} 이고 winner와 age는 하나의 로우를 특정짔는데 사용할 수 없으므로 non-prime attribute이다. 이 때 age는 candidate key에 일부분이 아닌 winner에 함수 종속성이 있으므로 제 2정규형 조건에도 어긋나지 않는다.

제 3정규형에 부합하기 위해선 모든 속성이 primary key인 id에 직접적인 함수 종속성이 있어야 하는데 age는 winner에 의해 특정되므로 이행적 함수 종속성을 가지고 있다. 이에 제 3정규형 조건에 부합하지 않는다.

이 경우 함수 종속 관계를 가진 name과 age는 유저 테이블로 빼고 이 테이블에는 user_id를 추가하여 foreign key를 부여하면 된다.

비정규화

비정규화는 정규형에 부합하는 테이블을 정규형을 지키지 않게 바꾸는 걸 의미한다.

정규화된 테이블을 비정규화하는 과정이 필요한 이유는 성능 개선 때문이다. 테이블이 여러개로 쪼개지고 데이터가 너무 많이 분산되어있으면 퍼져있는 데이터를 다시 모을 때 속도가 느려질 수 있다. 비정규화를 하면 이런 정보들을 한 테이블에 저장할 수 있어 조금 더 빠르게 조회가 가능한다.

그러나 비정규화된 테이블은 앞서 언급했든 데이터 이상 현상이 발생할 가능성이 높다. 따라서 비정규화를 시행해야 할 때는 1) 데이터가 너무 퍼져있어서 조회 성능 문제가 심각한 수준으로 일어나는 게 확실하고 2) 테이블을 삽입, 업데이트, 삭제하는 것보다 단순 조회 용도로만 사용하고 있을 때 적합하다.

0개의 댓글