[Index]
이상현상
정규화, 정규화 단계
반정규화
정규화 종류
이상현상
삽입 이상 (Insertion Anomaly)
새로운 데이터를 추가하기 위해 불필요한 데이터도 함께 삽입해야하는 상황.
ex) 특정 학생이 수강하는 과목 정보를 저장하는 테이블에서, 아직 수강한 과목이 없는 새 학생의 정보를 추가하려면
과목 정봉가 없는 상태로도 데이터를 추가해야하는 문제가 발생
삭제 이상 (Deletion Anomaly)
어떤 데이터를 삭제할 때, 의도하지 않은 다른 중요한 데이터까지 함께 삭제되는 상황.
ex) 학생과 그 학생이 수강하는 과목 정보를 동일한 테이블에 저장하고 있을 때,
특정 과목을 수강한 마지막 학생의 정보를 삭제하면 그 과목에 대한 정보까지 모두 삭제되는 문제가 발생.
갱신 이상 (Update Anomaly)
데이터의 일부를 수정하기 위해 여러 곳의 데이터를 동시에 수정해야 하는 상황.
ex) 학생들의 수강 과목과 강사 정보가 한 테이블에 저장되어 있을 때,
특정 강사가 담당하는 과목의 강사 정보를 변경하려면
모든 학생의 해당 과목 정보를 동시에 수정해야하는 문제가 발생.
이러한 이상 현상을 방지하기 위해,
데이터베이스 설계 과정에서 정규화를 통해
테이블을 적절히 분리하고 관계를 설정하여 데이터의 중복성을 최소화한다.
그럼 정규화라는게 무엇일까? 정처기 공부하면서 대략적으로 외우기만 했다.
정규화, 정규화 단계
데이터베이스를 사용할 때,
삽입, 삭제, 갱신 이상 현상이 없도록
ERD를 바꾸거나
테이블 설계 자체를 뜯어고치는(=테이블 분해) 과정.
[정규화의 목적]
1. 불필요한 데이터 중복을 제거하여 저장 공간을 효율적으로 활용한다
2. 데이터의 무결성을 유지한다.
3. 데이터베이스의 구조를 수정할 때 발생할 수 있는 이상 현상(Anomalies)을 최소화한다.
정규화는 데이터의 중복을 제거하고 무결성을 보장하는 것이 주 목적이지만,
실제로 데이터베이스를 설계할 때는 성능, 확장성, 관리 용이성 등 여러 가지 요소를 함께 고려해야 한다.
때로는 최대한의 정규화가 최선의 선택이 아닐 수도 있다.
정규화는 여러 단계(Normal Form)로 나누어지며, 각 단계마다 특정한 조건을 만족해야 합니다:
[정규화 단계] NF = normal form (각각의 rule)
제1정규형(1NF)
모든 컬럼의 값이 원자적이어야한다.
즉, 각 컬럼의 값은 더 이상 나눌 수 없는 단위로 되어야 한다.
하나의 컬럼에는 ,로 2개이상의 값이 올 수 없다.
[제1정규형은 다음과 같은 규칙을 만족해야한다.]
1) 1컬럼 1값
2) 1컬럼 1종류or타입의 값
3) 각 컬럼이 유일한 이름을 가진다.
4) 컬럼의 순서가 상관없어야한다.
[제1정규형 예시]
예시를 별도로 만들지 않아도
1~4까지의 규칙을 보면 알 수 있다.
제2정규형(2NF)
[제2정규형은 다음과 같은 규칙을 만족해야한다.]
1) 제1정규형을 만족하면서,
2) 기본키가 2개 이상의 컬럼으로 구성된 복합키를 가지고 있을 경우,
각 비키 컬럼은 기본 키의 전체에 대해 함수적 종속이어야한다. (모든 컬럼이 부분적 종속이 없어야한다.)
복합키로 PK를 설정하고나서
복합키의 PK중 키 값 하나로 다른 컬럼의 PK가 될 수 있는 경우.
[제2정규형 예시]
전체 컬럼은 학생번호, 과목, 지도교수, 성적 이렇게 4개가 있는 테이블이 있다.
위 테이블에서 기본키는 학생번호, 과목으로 복합키이다.
특정 성적의 값을 알기 위해서는 학생번호, 과목이 있어야한다.
특정 과목의 지도교수를 알기 위해서는 과목명만 알면된다.
복합키를 기본키로 가지고 있는데,
지도교수는 복합키 둘 다 사용해서 알아낼 수 있는게 아니라
복합키 두 개 중 하나의 컬럼으로 알아낼 수 있다.
따라서, 기본키 전체(복합키)에 대해
테이블에서 기본키에 포함되지 않은 컬럼의 값을 결정하지 않는 문제가 발생한다.
오직 기본키 중에서도 하나의 기본키로만 기본키가 아닌 컬럼의 값을 알아낼 수가 있는 것이다.
이것은 제2정규형의 규칙인 모든 컬럼이 부분적 종속이 없어야하는 것에
맞지 않는다.
그러므로, 제2정규형을 만족하려면
학생번호, 과목, 성적 3개의 컬럼을 가진 테이블 하나
과목, 지도교수 2개의 컬럼을 가진 테이블 하나
이렇게 분해해야한다.
[용어정리]
함수적 종속(함수 종속성 Functional Dependenc) : 한 컬럼의 값이 다른 컬럼의 값을 결정하는 관계를 의미.
ex) 학번 컬럼의 값이 주어져야 해당 학생의 이름을 정확히 알 수 있을 때,
학번에 대한 이름이 함수적 종속관계에 있다고 한다.
학번->이름
학번은 이름의 결정자
학번은 이름을 결정한다.
이름은 학번에 종속한다.
비키 : 테이블에서 기본키에 포함되지 않은 컬럼을 의미한다.
복합키 : 한 테이블 내에서 행의 유일성을 보장하는데 사용된다.
외래키 : 다른 테이블과의 관계를 정의하는데 사용된다.
[복합키와 외래키에 대한 예시]
학생 테이블 | Students |
학번 (PK) | student_id |
이름 | name |
전공 | major |
과목 테이블 | Courses |
과목코드 (PK) | course_code |
과목명 | course_name |
교수 | professor |
수강 테이블 | Enrollments |
학번 (FK) | student_id |
과목코드 (FK) | course_code |
성적 | grade |
학번과 과목코드 두 컬럼이 합쳐져서 복합키를 형성한다.
복합키는 수강 테이블의 기본키 역할을 한다.
한 학생이 여러 과목을 수강할 수 있고,
하나의 과목도 여러 학생에게 수강될 수 있기 때문에 두 정보를 조합해야
수강테이블에서 각 로우를 유일게 식별할 수 있다.
또한, 학번과 과목코드는 각각 학생 테이블과 과목 테이블을 참조하는 외래키로도 동작한다.
이 외래키들은 수강 테이블이 학생 테이블과 과목 테이블에 있는
데이터와 관계를 맺고 있음을 나타낸다.
이런 구조를 통해 각각의 로우가 한 학생과 한 과목 간의 관계
(즉, 한 학생이 어떤 과목을 수강하였는지를 유일하게 표현하게 된다.)
복합키를 사용하지 않게된다면
중복 수강 문제(동일한 조합이 여러번 등록되는 문제발생),
데이터 무결성 위반(동일한 데이터가 등록가능하니 수정, 삭제 시 문제발생),
비효율적인 쿼리 실행(모든 레코드를 검토해서 결과를 나타내기에 쿼리 성능 저하),
데이터 정확성 문제(한 학생이 과목을 여러번 수강, 과목을 수강하지 않았다는 정보가 동시 존재)가 발생할 수 있다.
제3정규형(3NF)
[제3정규형은 다음과 같은 규칙을 만족해야한다.]
1) 제2정규형을 만족하면서,
2) 비키 컬럼 간의 종속성, 즉 이행적 함수 종속성을 제거한다.
[제3정규형 예시]
학과명에 따라 담당교수가 정해져있고, 담당교수에 따라 강의실이 정해져있다면,
학과명을 알면 담당교수를 알 수 있다.
담당교수를 알면 강의실을 알 수 있다.
따라서, 학과명을 알면 강의실을 알 수 있다.
학과명 -> 담당교수 -> 강의실의 관계에서 이행적 함수 종속성이 발생한다.
결론적으로 이행적 함수 종속성이 존재하므로 제3정규형을 만족하지 않는다.
제3정규형을 만족하려면
학과명과 담당교수, 이 두 컬럼이 있는 테이블 하나
담당교수와 강의실 ,이 두 컬럼이 있는 테이블 하나
이렇게 분해해야한다.
[용어정리]
이행적 함수 종속성 : A->B, B->C의 종속 관계가 있을 때,
A->C의 종속 관계가 생기는 것을 의미한다.
BCNF(Boyce-Codd Normal Form)
[BCNF은 다음과 같은 규칙을 만족해야한다.]
1) 제 3정규형을 만족하면서,
2) 모든 결정자가 후보키의 부분집합이어야한다. (후보키 집합에 없는 컬럼이 결정자가 되어서는 안된다.)
[BCNF 예시]
학생번호, 과목 이 2가지가 기본키로 있는 테이블이 있다.
학생번호와 과목이 기본키로 지도교수를 알 수 있다.
하지만 같은 과목을 다른 교수가 가르칠 수도 있어서 과목->지도교수 종속은 성립하지 않는다.
지도교수가 어떤 과목을 가르치는지 알 수 있으므로 지도교수->과목 종속이 성립한다.
((내 의견.. 솔직히 지도교수도 여러 과목을 가르 칠 수 있는데.. 일단은 BCNF를 이해하기 위한 예시니까 그러겠지?
실제로 업무에서는 다양한 상황과 요구 사항을 고려해야겠지?))
이처럼 후보키 집합이 아닌 컬럼이 결정자가 되어버린 상황을
BCNF를 만족하지 않는다고 한다
BCNF를 만족하려면,
학생번호, 지도교수 이 2개의 컬럼이 있는 테이블 하나,
지도교수, 과목 이 2개의 컬럼이 있는 테이블 하나
이렇게 분해해야한다.
[용어정리]
후보키 : 슈퍼키 중에서 불필요한 속성을 제거하여 최소성을 만족시키는 키.
ex) 학생 테이블에서 '학번'은 후보키가 될 수 있다.
슈퍼키 : 한 테이블 내에서 튜플들을 유일하게 식별할 수 있는 하나 이상의 속성(컬럼)들의 집합.
즉, 유일성은 만족하지만 최소성은 만족시키지 않을 수 있다.
ex) 학생 테이블에서 '학번'이 튜플을 유일하게 식별할 수 있으므로 '학번'은 슈퍼키이다.
'학번', '이름'을 합친 {학번, 이름}도 슈퍼키이지만, '학번'만으로도 튜플을 유일하게 식별할 수 있기 때문에
'학번'+'이름'은 최소성을 만족시키지 않는다.
기본키 : 후보키 중에서 선택한 주 키이다. null값을 허용하지 않고, 유일해야한다.
한 테이블 내에서 튜플을 유일하게 식별할 수 있는 속성 또는 속성의 조합.
유일성과 최소성을 모두 만족시켜야한다.
테이블에서는 오직 하나의 기본키만을 가질 수 있다.
모든 기본키는 슈퍼키이지만, 모든 슈퍼키가 기본키인 것은 아니다.
대체키 : 후보키 중에서 기본키로 선택되지 않은 키.
ex) 학생 테이블에서 '이메일'이 후보키이지만 '학번'을 기본키로 선택했다면,
'이메일'은 대체키가 된다.
데이터베이스 설계에서는 주로 3NF나 BCNF까지의 정규화를 많이 사용한다.
너무 과도한 정규화는 성능 저하를 초래할 수 있기 때문에,
상황과 요구사항에 따라 적절한 단계까지만 정규화를 진행하는 것이 좋다.
정규화를 쉽게 하는 방법은
정규화 단계의 키워드를 기록해놓고
내가 만든 DB와 비교해가면서 보는 것이다.
제4정규형(4NF)
[제4정규형은 다음과 같은 규칙을 만족해야한다.]
1) BCNF를 만족하면서,
2) 다치 종속을 제거한다.
[용어정리]
다치 종속 : 한 속성이 다른 속성에 대해, 나머지 속성들을 고려하지 않고 종속될 때 발생한다.
ex) 학생이 여러 과목에 여러 번 수강신청을 할 수 있는 상황에서, "학생-수강신청한 과목"과 "학생-수강신청 날짜"가 있을 때,
학생이 어떤 과목을 언제 수강신청했는지는 중요하지 않다고 가정한다.
여기서 과목은 학생에 종속적(학생->과목)이고,
수강신청 날짜도 학생에 종속적(학생->수강신청 날짜)이다.
하지만 과목과 수강신청 날짜 사이에는 아무런 연관이 없다.
이런 관계를 다치 종속이라고 한다.
제5정규형(5NF or PJNF)
[제5정규형은 다음과 같은 규칙을 만족해야한다.]
1) 제4정규형을 만족하면서,
2) 조인 종속성을 통해 나타날 수 있는 이상 현상을 제거한다.
[용어정리]
조인 종속성 : 테이블을 여러 부분으로 나누고, 그 부분들을 다시 조인했을 때,
원래의 테이블과 동일하게 복구될 수 있을 때 발생한다.
ex) 테이블 A가 속성 X,Y,Z를 가지고 있을 때,
이 테이블 A를 두 부분 X,Y와 X,Z로 나누었다가
다시 합치면
원래의 테이블 A가 복구될 수 있다.
이런 경우를 조인 종속성이라고 한다.
정규화(=테이블 분해) <-> join
그럼, 정규화를 많이 할수록,
테이블이 많아진다. 그만큼 테이블 하나 단위로는 단순해질 것이다.
SQL할 때 힘들다. 테이블이 많이 엮여지면 시간이 많이 들고 성능이 떨어지고, join을 많이 해야하고...
테이블이 많아지니까 테이블의 관계도 복잡해진다.
따라서 반정규화가 등장했다.
반정규화
정규화해놓은 테이블 관계도가 너무 복잡해지기때문에
원상복구하는 반정규화를 한다.
join해서 쓸거면 합치라고 하는것이다.
그럼 클래스, dto, controller 등등 구현코드 뿐만 아니라 테스트코드, 요구사항분석부터 다 바꿔야하게된다.
프로젝트 전체를 바꿔야하기 때문에
전문가와 함께 처음부터 제대로 해놓는게 좋다.
이렇게 한다면, 차세대 버전으로 시스템 옮길때도 DB는 많이 안건드리게 된다.
* 다양하게 구글링을 통해 접한 지식을 개인적으로 계속 보기 위해 정리했습니다.
'개인 공부 (23.07~' 카테고리의 다른 글
[JPA] dirty checking 더티체킹 간단 이해 (0) | 2023.09.09 |
---|---|
[JAVA] Logging Framework / Slf4j (0) | 2023.09.08 |
EntityManager(순수 JPA) vs JpaRepository(Spring Data JPA) (0) | 2023.09.04 |
[SpringBoot] application.properties 무엇인가 (0) | 2023.09.04 |
[SQL] DDL DML DCL 개념 (0) | 2023.09.04 |