
정규화: 정규화란 데이터베이스 설계에서 데이터간의 중복을 제거하여 데이터의 무결성(Intergrity)을 유지하고 저장 공간을 절약하며 데이터베이스에서 발생할 수 있는 모든 이상현상을 방지하기 위해 실행하는 데이터 모델링의 과정이다.
주로 단계별로 문제가 있는 테이블의 특정 데이터들을 기존의 다른 테이블로 옮기거나 새로운 테이블을 만들어 옮기는 등 테이블을 분해해가며 이루어진다.
일반적으로 정규화는 데이터 모델을 만들고 난 이후에 직접 데이터베이스를 구현하기 전에 이루어진다.
정규형: 정규형은 데이터베이스 테이블이 정규화 과정에서 도달할 수 있는 각 구조적 단계들을 의미한다. 각 정규형은 특정한 데이터 구조를 만족시키기 위한 규칙이나 제약 조건을 정의하며, 데이터의 중복을 줄이고 무결성을 유지하기 위한 목적으로 사용된다.
정규형은 주로 1NF, 2NF, 3NF 등의 각 단계의 숫자를 섞을 약자로 표시되고 순서에 따라 지켜야할 규칙이 누적된다.
예를 들어 2NF는 1NF를 만족하고, 3NF는 1, 2NF를 만족해야 하는 식으로 누적된다.
일반적으로 3NF 이후에도 4NF, 5NF, BCNF, EKNF 등 다양한 단계의 정규형이 존재하지만 데이터베이스를 전문적으로 연구하는 직업이 아니고서는 잘 다룰 일이 없다.
대부분의 경우 3NF까지만 잘 부합한다면 충분히 잘 정규화된 데이터베이스라고 할 수 있다.
1NF: 테이블 안의 모든 값들이 원자값(나눌 수 없는 하나의 값, Atomic Value)을 갖도록 테이블을 분해해나가는 정규화 과정이다.
| id | name | company | company_phone_number | address |
|---|---|---|---|---|
| 1 | 홍길동 | NEVER | 356-213-134 | 서울, 제주 |
| 2 | 김철수 | CACAO | 409-234-658 | 부산 |
| 3 | 박영희 | SAMSONG | 397-346-379 | 수원, 용인 |
그렇다면 어떻게 해당 테이블을 1NF에 부합하게 수정할 수 있을까??
| id | name | company | company_phone_number | address |
|---|---|---|---|---|
| 1 | 홍길동 | NEVER | 356-213-134 | 서울, 제주 |
| 2 | 김철수 | CACAO | 409-234-658 | 부산 |
| 3 | 박영희 | SAMSONG | 397-346-379 | 수원, 용인 |
address table
| id | user_id | address |
|---|---|---|
| 1 | 1 | 서울 |
| 2 | 3 | 용인 |
| 3 | 1 | 제주 |
| 4 | 2 | 부산 |
| 5 | 3 | 수원 |
제 2정규화(2NF)에 대해 알아보기 전에 먼저 이해해야하는 개념인 '함수 종속성'과 후보키(Candidate Key)에 대해 살펴보자.
함수 종속성 : 함수 종속성이란 하나의 속성(컬럼)이 다른 속성이 결정되는 데에 결정적인 역할을 하는 관계이다.
예를 들어 y = 2x + 3와 같은 함수 식에서 x에 어떤 값이 들어가냐에 따라 y의 값이 결정되기 때문에 y는 x에 대해 함수 종속성이 있다고 표현할 수 있다.
(x → y)
데이터베이스 테이블 예시를 통해 살펴보자.
| 이름 (name) | 나이 (age) | 직업 (occupation) |
|---|---|---|
| 김철수 | 30 | 개발자 |
| 이영희 | 25 | 디자이너 |
| 박민수 | 40 | 마케팅 매니저 |
| 최수정 | 35 | 회계사 |
| 정우진 | 28 | 엔지니어 |
이름이 주어지면 해당 인물의 나이와 직업이 고유하게 결정되기 때문에 나이(age)와 직업(occupation)은 이름(name)에 함수 종속성이 있다고 할 수 있다.
(이름 (Name) → {나이 (Age), 직업 (Occupation)})
이행적 함수 종속: 이행적 함수 종속성은 마치 3단 논법처럼 하나 이상의 attribute(속성)을 건너서 함수 종속성이 있는 경우이다.
| 이름 (name) | 국적 (nationality) | 대륙 (continent) |
|---|---|---|
| 김철수 | 한국 | 아시아 |
| 마리아 | 스페인 | 유럽 |
| 존 스미스 | 미국 | 북아메리카 |
| 아야카 | 일본 | 아시아 |
| 피에르 | 프랑스 | 유럽 |
국적(nationality)은 사람(name)에 의해 결정되고 또 어느 대륙(continent)에 사는지는 국적에 의해 결정된다.
(이름 → 국적 → 대륙)
Candidate Key(후보 키): 하나의 로우(데이터)를 고유하게 식별할 수 있는 속성(컬럼, attribute)들의 최소 집합을 의미한다.
학생 테이블
| 학번 (StudentNumber) | 이름 (Name) | 학과 (Department) |
|---|---|---|
| 20231001 | 홍길동 | 컴퓨터공학과 |
| 20231002 | 김철수 | 전자공학과 |
| 20231003 | 박영희 | 기계공학과 |
| 20231004 | 이영수 | 수학과 |
| 20231005 | 최미나 | 물리학과 |
위의 테이블에서는 학번은 학생당 1개만 가질 수 있기 때문에 학번이 후보 키가 될 수 있다.
학생 테이블
| 학생ID (StudentID) | 강좌ID (CourseID) | 학기 (Semester) | 성적 (Grade) |
|---|---|---|---|
| 101 | CSE101 | 2024-1 | A |
| 101 | CSE102 | 2024-1 | B |
| 102 | CSE101 | 2024-1 | B+ |
| 103 | CSE101 | 2024-2 | A- |
| 101 | CSE101 | 2024-2 | A |
위의 테이블에서는 학생ID와 강좌ID의 값만으로는 개별 학생을 식별할 수는 없지만 한 강좌에서 학생ID는 중복되지 않는다고 하면 학생ID와 강좌ID를 두 개를 합치면 학생을 식별할 수 있기 때문에 (학생ID, 강좌ID)가 후보 키가 될 수 있다.
Prime Attibute: Candiate Key에 포함된 모든 attirbute
Non-Prime Attribute: Candiate Key 포함되지 않는 모든 attribute
2NF: 2NF를 충족하기 위해서는 1NF에 부합하고 테이블 안의 후보키(candidate key)의 일부분에 대해서만 함수 종속성(부분 함수 종속성)이 있는 non-prime attribute가 없어야 한다.
수강 신청 테이블
| 학번 (Student ID) | 과목 코드 (Course Code) | 교수 이름 (Professor Name) |
|---|---|---|
| 20230101 | CS101 | 김교수 |
| 20230101 | MATH202 | 이교수 |
| 20230102 | CS101 | 김교수 |
| 20230103 | BIO303 | 박교수 |
| 20230103 | MATH202 | 이교수 |
예시 테이블을 살펴보자.
기본 키이자 후보 키가 학번(Student ID)와 과목 코드(Course Code)의 복합 키로 되어 있는데 non-prime attribute인 교수 이름(Professor Name)이 과목 코드(Course Code)에만 종속되기 때문에 부분 함수 종속성이 존재하여 해당 테이블은 2NF를 위배한다.
제2정규형(2NF)을 지키기 위해서는 부분 함수 종속성을 제거해야 한다.
부분 함수 종속성을 제거하는 방법은 간단하다.
후보키의 일부분에만 의존하는 Non-prime attribute를 따로 분리해주면 된다.
수강 신청 테이블
| 학번 (Student ID) | 과목 코드 (Course Code) |
|---|---|
| 20230101 | CS101 |
| 20230101 | MATH202 |
| 20230102 | CS101 |
| 20230103 | BIO303 |
| 20230103 | MATH202 |
과목 정보 테이블
| 과목 코드 (Course Code) | 교수 이름 (Professor Name) |
|---|---|
| CS101 | 김교수 |
| MATH202 | 이교수 |
| BIO303 | 박교수 |
만약 해당 속성을 옮겨줄 테이블이 존재한다면 해당 테이블에 옮기고 존재하지 않는다면 지금처럼 새로운 테이블을 만들어 옮기면 된다.
3NF: 3NF는 2NF에 부합하고 동시에 테이블 안의 모든 attribute(속성, 컬럼)들은 오직 Primary Key에 대해서만 함수 종속성이 있어야 한다.
직원 (Employees) 테이블
| 직원ID (Employee ID) | 직원 이름 (Employee Name) | 부서ID (Department ID) | 부서 이름 (Department Name) | 부서 위치 (Department Location) |
|---|---|---|---|---|
| 1001 | 김철수 | D01 | 인사부 | 서울 |
| 1002 | 박영희 | D02 | 재무부 | 부산 |
| 1003 | 이민호 | D01 | 인사부 | 서울 |
| 1004 | 조지훈 | D03 | 마케팅부 | 대구 |
해당 테이블은 2NF까지는 충족하지만 PK는 직원ID인데에 반해 부서 이름과 부서 위치는 오히려 PK가 아닌 부서ID에 종속되기 때문에 3NF에 위배된다.
이러한 경우도 계속 그래왔던 것처럼 테이블을 분해하여 해결할 수 있다.
직원 (Employees) 테이블
| 직원ID (Employee ID) | 직원 이름 (Employee Name) | 부서ID (Department ID) |
|---|---|---|
| 1001 | 김철수 | D01 |
| 1002 | 박영희 | D02 |
| 1003 | 이민호 | D01 |
| 1004 | 조지훈 | D03 |
부서 (Departments) 테이블
| 부서ID (Department ID) | 부서 이름 (Department Name) | 부서 위치 (Department Location) |
|---|---|---|
| D01 | 인사부 | 서울 |
| D02 | 재무부 | 부산 |
| D03 | 마케팅부 | 대구 |
비정규화: 비정규화는 데이터베이스의 성능을 개선하기 위해 일부 정규화된 테이블을 의도적으로 비정규화하는 방식이다. 비정규화는 정규화된 데이터베이스 구조를 변경하여 데이터 중복을 허용하거나 조인 연산을 줄이는 방식으로 수행된다. 비정규화는 주로 읽기 성능을 최적화하고, 복잡한 쿼리의 성능을 향상시키기 위한 목적으로 사용된다.
중복 데이터 저장: 자주 조회되는 데이터는 여러 테이블에 중복 저장하여 조인 없이 직접 조회할 수 있습니다.
집계 테이블: 자주 사용하는 집계 결과를 미리 계산하여 별도의 테이블에 저장합니다. 예를 들어, 매출 집계 테이블을 만들어서 월별 매출을 미리 계산해 저장합니다
읽기 성능 향상: 비정규화된 데이터베이스는 자주 사용되는 쿼리의 성능을 향상시킬 수 있다. 데이터가 중복 저장되면 조인 연산을 줄일 수 있어 조회 성능이 향상된다.
쿼리 단순화: 비정규화는 복잡한 쿼리를 단순화할 수 있다. 여러 테이블을 조인하는 대신 단일 테이블에서 필요한 데이터를 직접 조회할 수 있다.
트랜잭션 성능 개선: 데이터 중복으로 인해 읽기 작업이 빨라지므로, 트랜잭션의 성능이 개선될 수 있다.
데이터 중복: 데이터 중복이 발생하여 데이터 일관성을 유지하는 것이 어려울 수 있. 데이터 수정 시 여러 테이블을 업데이트해야 할 수도 있다.
저장 공간 증가: 데이터 중복으로 인해 저장 공간이 증가할 수 있다.
복잡한 업데이트: 데이터가 중복되어 있기 때문에 업데이트 시 여러 곳에서 수정해야 할 수 있다.