관계형 데이터베이스에서 1:M 관계에 대해 알아보자.
1:M 관계는 부자지간 관계이다.
1:M 관계를 부자지간으로 볼 수 있다. 가장 중요하고 근본적인 관계이다. 가장 흔하게 나타나는 일반적인 관계이기도 하다.
- 파일(트리) 구조도 1:M 관계이다.
❓만약 자식과 손자라는 테이블이 또 다시 1:M 관계를 맺는다고 했을 때, 부모와 손자간의 관계가 존재할까???
답은, NO 이다.
✅ 관계는 3개 테이블 간의 관계를 표현하지 않는다. 반드시 2개 테이블 간의 관계에만 관심이 있다.
PK & FK
- 부모 테이블의
PK가 자식 테이블에서 표현되는 것을FK(foreign key)라고 한다. - 자식 테이블의
FK는 중복될 수 있지만 부모 테이블의PK는 반드시 하나이다.
🚨 권고사항
PK 명칭과 FK 명칭은 일치하도록 사용하는 것이 좋다.
⭐️ 관계형 데이터베이스(RDB)는 부모 없는 자식을 막아준다.
부모 없는 자식을 막아준다는 것이 무슨 뜻일까?
RDBMS는 부모 없는 자식의 관계를 허용하지 않는다. 따라서 다음과 같은 제약이 존재한다.
- 어떤 부모의
PK가 자식 테이블의FK로 사용될 때, 해당 부모의 데이터를DELETE할 수 없다. 해당 부모를 삭제하면 해당 부모의PK를FK로 들고 있는 자식이 고아가 되기 때문이다. - 부모 없는 자식은
INSERT할 수 없다. 즉,FK가 걸려있는 테이블에 데이터를 삽입할 때는FK를 반드시 설정해줘야지만INSERT할 수 있다.
❓만약 부모 없는 자식 데이터가 생기면 무슨 일이 벌어질까.
부모 없는 자식 데이터는 가비지 데이터(Garbage Data)가 된다. outer join으로 조회할 때 가비지 데이터가 드러나게 된다. 가비지 데이터가 생기면 COUNT 함수와 같은 집계 함수 결과에 치명적인 오차를 발생시킨다.
✅ 프로그래밍으로 가비지 데이터를 커버치는 행위는 쓰레기 코드를 만드는 것이다. 따라서, DB 스키마 설계 시 관계를 잘 설정해서 가비지 데이터가 생기지 않도록 하는 것이 매우 중요하다.
1:M 관계 PK, FK 설정 예제
학년과 반이라는 테이블들을 설계해보자. 학년과 반은 1:M 관계를 지닌다.
학년 테이블
학년 번호를 PK로 설정했다.
| 열이름 | 데이터 타입 | NULL 허용 | |
|---|---|---|---|
| PK | 학년번호 | tinyint | x |
| 학년이름 | varchar(50) | o |
1 | |
| 학년번호 | 학년이름 | |
|---|---|---|
| 1 | 1 | 1학년 |
| 2 | 2 | 2학년 |
| 3 | 3 | 3학년 |
반 테이블
현재 PK만 설정되어 있다.
💡 학년번호와 반번호를 함께 복합키로 설정한 이유는 학년별로 반 번호를 1반 부터 시작하도록 하기 위함이다. 만약, 반 테이블의 PK로 반 번호만을 설정하면 반 번호의 중복이 불가능하기 때문에 학년 별로 1반부터 시작하지 못한다. 이처럼 복합키를 이용하면 두 번째 필드(반 번호)의 중복을 허용할 수 있게 된다.
| 열이름 | 데이터 타입 | NULL 허용 | |
|---|---|---|---|
| PK | 학년번호 | tinyint | x |
| PK | 반번호 | tinyint | x |
| 반이름 | varchar(50) | o |
1 | |
두 번째 행(row) 데이터는 부모가 없다.
| 학년번호 | 반번호 | 반이름 | |
|---|---|---|---|
| 1 | 1 | 1 | 1반 |
| 2 | 4 | 1 | 1반 |
외래 키 설정이 없기 때문에 부모 없는 자식 데이터(가비지 데이터)가 삽입되어 버렸다.
가비지 데이터를 우선 삭제해주자.
1 | |
학년 테이블의 학년번호를 반 테이블의 학년번호의 외래키로 설정해준다.
| 열이름 | 데이터 타입 | NULL 허용 | |
|---|---|---|---|
| PK, FK | 학년번호 | tinyint | x |
| PK | 반번호 | tinyint | x |
| 반이름 | varchar(50) | o |
다시 INSERT 구문을 실행해보면
1 | |
✅ FOREIGN KEY 제약 조건에 위배되는 데이터는 INSERT 되지 않는다. 즉, FK가 설정된 테이블은 해당 FK 값이 부모 테이블의 PK 값으로 존재하는지 먼저 탐색한다. 그러고나서 부모(PK)가 정상적으로 있는 경우에만 INSERT가 수행된다.
| 학년번호 | 반번호 | 반이름 | |
|---|---|---|---|
| 1 | 1 | 1 | 1반 |
| 2 | 3 | 1 | 1반 |
학년과 반 테이블을 조인하여 한 번에 조회해보자.
1 | |
| 학년번호 | 학년이름 | 학년번호 | 반번호 | 반이름 | |
|---|---|---|---|---|---|
| 1 | 1 | 1학년 | 1 | 1 | 1반 |
| 2 | 3 | 3학년 | 3 | 1 | 1반 |
요약 및 정리
- 1:M 관계는 부자지간 관계이다.
- PK & FK 설정이 제대로 되어 있다면 RDB는 부모 없는 자식 데이터 발생을 막아준다.
- PK 설정 전략에 따라 특정 필드의 중복을 허용할 수도 있고 방지할 수도 있다.