선택 심화: 다치 종속과 무손실 분해 반례
4NF·5NF의 전제를 독립 사실과 세 관계 JOIN 반례로 이해하고 테이블을 나누기 전에 무손실 조건을 확인합니다.
이 절은 앞의 함수 종속·3NF·BCNF를 이해한 뒤 읽는 선택 심화입니다. 번호가 높다는 이유로 모든 테이블을 계속 나누는 단계가 아닙니다. 한 사람의 기술과 사용 언어처럼 서로 독립적인 여러 값이 같은 표에 저장될 때 함수 종속만으로 설명하기 어려운 반복이 생깁니다.
4NF와 다치 종속
민이 기술 Python·SQL, 언어 한국어·영어를 갖고 두 목록이 서로 독립이라는 업무 규칙을 가정합니다. 모든 기술–언어 조합을 표로 표현하면 네 행입니다.
| 사람 | 기술 | 언어 |
|---|---|---|
| 민 | Python | 한국어 |
| 민 | Python | 영어 |
| 민 | SQL | 한국어 |
| 민 | SQL | 영어 |
사람은 기술 하나를 함수적으로 결정하지 않습니다. 대신 사람별 기술 집합이 언어와 독립적으로 조합된다는 규칙을 다치 종속 사람 →→ 기술로 표현합니다. 관계 R의 비자명한 다치 종속 X→→Y에서 X가 슈퍼 키여야 4NF입니다. X∪Y가 전체 속성이거나 Y가 X 안에 있는 자명한 경우는 제외합니다.
이 예의 사람은 한 행의 기술·언어를 모두 결정하지 않으므로 슈퍼 키가 아닙니다. (사람,기술)과 (사람,언어)로 나누면 기술 두 행·언어 두 행을 각각 한 번 저장하고 JOIN으로 네 조합을 복원합니다. 새 기술 Rust 하나를 추가할 때 언어 수만큼 같은 기술을 여러 번 쓰지 않아도 됩니다.
CREATE DATABASE IF NOT EXISTS cs_db_lab;
USE cs_db_lab;
DROP TABLE IF EXISTS cs_nf_skills;
DROP TABLE IF EXISTS cs_nf_languages;
CREATE TABLE cs_nf_skills(person VARCHAR(20),skill VARCHAR(20),PRIMARY KEY(person,skill));
CREATE TABLE cs_nf_languages(person VARCHAR(20),language VARCHAR(20),PRIMARY KEY(person,language));
INSERT INTO cs_nf_skills VALUES('민','Python'),('민','SQL');
INSERT INTO cs_nf_languages VALUES('민','한국어'),('민','영어');
SELECT s.person,s.skill,l.language FROM cs_nf_skills AS s
JOIN cs_nf_languages AS l ON l.person=s.person ORDER BY s.skill,l.language;결과는 네 조합입니다. 이것이 원래의 정상 관계를 복원하는 이유는 기술과 언어가 독립이라는 전제입니다. “민은 Python으로 영어 프로젝트만, SQL로 한국어 프로젝트만 수행한다”처럼 기술–언어 조합 자체가 사실이면 두 표로 나눈 JOIN은 없던 조합을 만들어 잘못됩니다. 현재 두 행만 보고 독립성을 가정하지 않습니다.
5NF와 조인 종속
조인 종속은 관계를 여러 투영으로 나누고 JOIN했을 때 원래 관계와 정확히 같아지는 조건입니다. 5NF는 비자명한 조인 종속이 후보 키에 의해 함의되는 경우만 허용합니다. 후보 키·다치 종속·조인 종속은 업무상 가능한 모든 관계를 대상으로 판단하며 한 데이터 샘플의 우연한 JOIN 일치만으로 증명하지 않습니다.
공급자–부품–프로젝트 관계를 공급자/부품, 공급자/프로젝트, 부품/프로젝트의 세 쌍으로 나누면 항상 무손실일까요? 다음은 그렇지 않은 반례입니다.
DROP TABLE IF EXISTS cs_nf_supplies;
CREATE TABLE cs_nf_supplies(
supplier VARCHAR(10),part VARCHAR(10),project VARCHAR(10),
PRIMARY KEY(supplier,part,project)
);
INSERT INTO cs_nf_supplies VALUES('A','X','P'),('A','Y','Q'),('B','X','Q');
WITH sp AS (SELECT DISTINCT supplier,part FROM cs_nf_supplies),
sj AS (SELECT DISTINCT supplier,project FROM cs_nf_supplies),
pj AS (SELECT DISTINCT part,project FROM cs_nf_supplies)
SELECT sp.supplier,sp.part,sj.project
FROM sp JOIN sj ON sj.supplier=sp.supplier
JOIN pj ON pj.part=sp.part AND pj.project=sj.project
ORDER BY sp.supplier,sp.part,sj.project;원본은 세 행인데 결과는 네 행입니다. 새로 생긴 (A,X,Q)는 세 쌍 각각에는 근거가 있지만 실제 공급 사실로는 저장된 적이 없습니다. 가짜 튜플은 SQL 오류 없이도 생깁니다. 세 쌍의 유일성을 선언해도 이 조합의 의미를 자동 증명하지 못합니다.
업무 규칙이 “세 쌍이 모두 허용되면 그 삼중 공급도 항상 허용”이라면 (A,X,Q)까지 포함하는 관계여야 합니다. 이 규칙에서 세 투영을 통한 무손실 분해를 논의할 수 있지만, 실제 계약처럼 삼중 조합을 별도로 승인한다면 원래 삼중 관계를 유지해야 합니다. 허용 가능한 조합과 실제 발생 기록도 섞지 않습니다.
독립한 목록 두 개와 실제 조합 사실을 구분한다
한 사람의 기술 2종과 언어 2종이 독립이라는 업무 전제가 있을 때만 네 조합을 복원합니다.
분해 검수의 순서
기호를 예제의 키·중간 표로 풀기
4NF 예제의 전체 속성을 R={사람,기술,언어}, X={사람}, Y={기술}로 놓습니다. Y가 X 안에 없고 X∪Y도 전체 R이 아니므로 사람 →→ 기술은 비자명합니다. 사람만으로는 같은 사람의 네 행을 구별할 수 없어 슈퍼 키가 아닙니다. 업무 규칙에 다른 함수 종속이 없다고 하면 세 속성을 함께 가져야 튜플을 구별합니다. 이것이 위 관계가 4NF 조건을 어기는 지점입니다.
두 투영은 (민,Python),(민,SQL)과 (민,한국어),(민,영어)입니다. 같은 사람의 기술–언어 조합을 교차해 네 행을 만듭니다. 다치 종속의 독립성은 같은 사람의 두 튜플에서 기술과 언어를 서로 바꾼 튜플도 유효 관계에 있어야 한다는 조건입니다. 이 규칙이 모든 유효 관계에 적용될 때 분해의 JOIN이 없던 사실을 추가하지 않습니다. 두 특정 조합만 승인한 규칙에는 이 조건이 성립하지 않습니다.
5NF 반례의 세 투영은 다음과 같습니다.
| 투영 | 원본 세 행에서 남는 쌍 |
|---|---|
| 공급자/부품 SP | AX, AY, BX |
| 공급자/프로젝트 SJ | AP, AQ, BQ |
| 부품/프로젝트 PJ | XP, YQ, XQ |
SP와 SJ를 공급자로 먼저 결합하면 AXP·AXQ·AYP·AYQ·BXQ의 다섯 후보입니다. PJ에 없는 YP 쌍을 제외하면 AXP·AXQ·AYQ·BXQ의 네 튜플이 남습니다. AXQ는 AX·AQ·XQ가 각 투영에 있어 생성되지만 원본에는 없습니다.
여기에 AXQ를 채운 네 행에서는 SP가 프로젝트를 결정하지 못하고(AX→P/Q), SJ가 부품을 결정하지 못하고(AQ→X/Y), PJ가 공급자를 결정하지 못합니다(XQ→A/B). 다른 함수 종속이 없다는 업무 가정에서 후보 키는 세 속성 전체입니다. 세 속성 키의 유일성만으로 이 세 투영의 조인 종속은 함의되지 않습니다. 원래 세 행도 전체 키는 유일하지만 JOIN 일치가 깨지기 때문입니다. 반대로 “세 쌍의 허용이 곧 삼중 허용”을 모든 유효 관계의 규칙으로 정하면 비자명 조인 종속이 별도로 생기며 그 규칙에 맞춰 세 쌍의 표로 분해할 수 있습니다. 샘플에 AXQ를 추가하는 것과 업무 규칙을 채택하는 것은 다른 결정입니다.
이 전개는 이 예제의 키·종속성·중간 JOIN을 판정한 것입니다. 임의 스키마의 모든 후보 키와 모든 조인 종속을 찾는 일반 알고리즘/증명을 대신하지 않습니다. 교재 저자 정규화 슬라이드
- 속성의 뜻과 모든 후보 키를 적습니다.
- 함수/다치/조인 종속을 업무 규칙으로 적습니다.
- 현재 데이터가 그 규칙을 지키는지 확인합니다.
- 분해한 투영을 JOIN하고 누락과 가짜 튜플을 각각 검사합니다.
- 분해 뒤 중요한 제약을 어디에서 강제할지 결정합니다.
현재 등급 Gold의 할인율이 20%와 5% 두 값이면 등급 → 할인율 규칙을 위반합니다. 그 상태를 정상 관계로 가정하고 정규형 분해를 진행하면 안 됩니다. 데이터 교정 또는 “등급·기간별 할인” 같은 실제 규칙의 재정의가 먼저입니다. 앞의 함수 종속 본문은 이런 업무 가정과 무손실·종속 보존을 구분합니다.
독립 문제
기술/언어 예제에서 민의 Rust 기술을 추가하면 분리 표의 추가 행 수와 JOIN의 증가 행 수는 각각 얼마인가요? 공급 반례에서 (A,X,Q)를 추가했을 때 현재 샘플 JOIN이 일치한다는 이유만으로 앞으로 모든 데이터도 안전하다고 할 수 있나요?
힌트
독립 목록의 곱과 업무 규칙의 범위를 분리합니다.
풀이
기술 표에는 Rust 한 행을 추가하고 언어 두 개와 조합되므로 JOIN에는 두 행이 늘어 전체 6행입니다. 공급 샘플은 추가 뒤 네 행으로 일치하지만 앞으로 가능한 관계에 대한 조인 종속을 업무 규칙으로 확인해야 무손실성을 일반화할 수 있습니다. 일치한 샘플 하나는 일반 증명이 아닙니다.
높은 정규형은 외우는 순서보다 독립 사실과 실제 조합 사실을 구별하는 도구입니다. 분해 이유와 복원 조건을 설명할 수 있을 때만 적용합니다.