Part 2의 마지막 강입니다. 관계와 상속 구조를 테이블로 옮길 때의 선택지를 정리합니다.
1. 식별 관계 vs 비식별 관계
| 구분 | 식별 관계 | 비식별 관계 |
|---|---|---|
| 정의 | 부모 PK가 자식 PK의 일부 | 부모 PK가 자식의 일반 컬럼(FK) |
| 자식의 존재 | 부모에 강하게 종속 | 상대적으로 독립 |
| IDEF1X 표기 | 실선 | 점선 |
| 예시 | 항목처리(item_id, stage_code) | 주문항목(item_id PK, order_no FK) |
2. 식별 관계의 장단점
장점
- 부모 키로 자식을 조회할 때 PK 인덱스를 그대로 사용 (InnoDB에서는 물리적으로 인접 저장)
- 조부모 키가 손자까지 전파되므로 중간 조인 없이 필터 가능
- "부모 없이 존재할 수 없다"는 의미가 구조로 드러남
단점
- 5강에서 본 PK 크기 전파
- 부모 PK가 바뀌면 모든 자손의 PK가 바뀜
- 관계가 나중에 1:N에서 N:M으로 바뀌면 PK 재설계 필요
3. 선택 기준
| 질문 | 식별 쪽 | 비식별 쪽 |
|---|---|---|
| 자식이 부모 없이 의미가 있는가? | 없음 | 있음 |
| 자식이 다른 부모로 옮겨갈 수 있는가? | 없음 (항목처리의 소속 주문항목은 불변) | 있음 (주문항목의 출고건 재배정, shipment_no 변경) |
| 계층 깊이가 3단계 이상인가? | 아니오 | 예 → 중간부터 대리키 |
| 부모 키로 범위 조회가 핵심인가? | 예 | 아니오 |
| ORM을 주로 사용하는가? | — | 예 (복합키 매핑 비용) |
실무 패턴: 약한 엔티티와 교차 테이블은 식별 관계, 독립적인 업무 엔티티는 대리키 + 비식별 관계. 계층이 깊어지면 2단계까지만 식별하고 그 아래는 대리키로 끊습니다.
4. 슈퍼타입/서브타입
예제: 검수(inspection)는 외관(VI), 중량(WT), 포장(PK) 으로 나뉩니다. 공통 속성(검수 일자, 검수자, 판정)과 유형별 속성이 있습니다.
| 유형 | 고유 속성 |
|---|---|
| VI | 사진 수, 결함 등급 |
| WT | 측정 무게, 허용 범위, 측정 장치 번호 |
| PK | 포장자재 종류 |
개념 모델에서 두 가지 성질을 먼저 정합니다.
- 배타(Exclusive) vs 중복(Overlapping): 하나의 검수가 두 유형일 수 있는가?
- 완전(Total) vs 부분(Partial): 세 유형 외의 검수가 있는가?
5. 세 가지 변환 전략
전략 A: 단일 테이블 (Single Table / Roll-up)
sql
CREATE TABLE inspection (
inspection_id BIGINT PRIMARY KEY,
inspect_type CHAR(2) NOT NULL CHECK (inspect_type IN ('VI','WT','PK')),
inspect_date DATE NOT NULL,
result CHAR(1) NOT NULL,
-- VI
vi_photo_count INT,
vi_defect_grade CHAR(1),
-- WT
wt_measured_g NUMERIC(8,2),
wt_tolerance_g NUMERIC(8,2),
-- PK
pk_material VARCHAR(20),
CHECK (inspect_type <> 'VI' OR vi_photo_count IS NOT NULL),
CHECK (inspect_type <> 'WT' OR wt_measured_g IS NOT NULL)
);전략 B: 서브타입별 테이블 (Table per Concrete Type / Roll-down)
sql
CREATE TABLE inspection_vi (inspection_id BIGINT PRIMARY KEY, inspect_date DATE, result CHAR(1), photo_count INT, defect_grade CHAR(1));
CREATE TABLE inspection_wt (inspection_id BIGINT PRIMARY KEY, inspect_date DATE, result CHAR(1), measured_g NUMERIC(8,2), tolerance_g NUMERIC(8,2));
CREATE TABLE inspection_pk (inspection_id BIGINT PRIMARY KEY, inspect_date DATE, result CHAR(1), material VARCHAR(20));전략 C: 슈퍼타입 + 서브타입 테이블 (Table per Type / 1:1)
sql
CREATE TABLE inspection (
inspection_id BIGINT PRIMARY KEY,
inspect_type CHAR(2) NOT NULL,
inspect_date DATE NOT NULL,
result CHAR(1) NOT NULL,
UNIQUE (inspection_id, inspect_type)
);
CREATE TABLE inspection_vi (
inspection_id BIGINT PRIMARY KEY,
inspect_type CHAR(2) NOT NULL DEFAULT 'VI' CHECK (inspect_type = 'VI'),
photo_count INT NOT NULL,
defect_grade CHAR(1),
FOREIGN KEY (inspection_id, inspect_type)
REFERENCES inspection (inspection_id, inspect_type)
);inspect_type을 서브타입에도 두고 복합 FK를 거는 기법으로, VI 서브타입 행이 WT 슈퍼타입 행을 참조하는 것을 막습니다 (배타성 보장).
6. 전략 비교
| 항목 | A. 단일 테이블 | B. 서브타입별 | C. 슈퍼+서브 |
|---|---|---|---|
| 전체 목록 조회 | 빠름 | UNION 필요 | 슈퍼타입만 조회 |
| 유형별 상세 조회 | 빠름 | 빠름 | 조인 1회 |
| NULL 컬럼 | 많음 | 없음 | 없음 |
| 유형별 NOT NULL 강제 | CHECK로 가능 | 직접 가능 | 직접 가능 |
| 공통 속성 FK 참조 | 쉬움 | 어려움 (참조 대상이 3개) | 쉬움 |
| 유형 추가 | 컬럼 추가 | 테이블 추가 | 테이블 추가 |
| 전역 ID 유일성 | 자동 | 별도 시퀀스 공유 필요 | 자동 |
| JPA 매핑 | SINGLE_TABLE | TABLE_PER_CLASS | JOINED |
선택 가이드
- 서브타입 고유 속성이 적고, 전체 조회가 많다 → A
- 다른 테이블이 "검수"를 참조한다 (예: 결함 → 검수) → C (B는 FK를 걸 수 없음)
- 유형 간 공통 처리가 거의 없고, 각각 독립적으로 쓰인다 → B
- 확신이 없으면 → C가 가장 무난한 기본값
11강 정리
- 식별 관계는 종속성과 조회 효율을, 비식별 관계는 유연성과 키 크기를 얻는다.
- 계층이 깊으면 중간에서 대리키로 식별 관계를 끊는다.
- 슈퍼/서브타입은 단일·서브타입별·슈퍼+서브 세 전략이 있고, 외부 참조 여부가 가장 중요한 기준이다.
- 복합 FK에 유형 컬럼을 포함하면 배타적 서브타입을 DB로 강제할 수 있다.