키의 종류
릴레이션에서 각 튜플을 유일하게 식별하는 방법이 필요합니다. 김철수라는 이름은 중복될 수 있지만, 학번 2024001은 유일합니다. 이처럼 튜플을 구분하는 속성 또는 속성의 조합을 키(Key)라 합니다. 키는 관계형 데이터베이스의 핵심 개념으로, 데이터의 무결성을 보장하고 테이블 간의 관계를 형성하는 근본적인 메커니즘입니다.
키의 두 가지 성질
모든 키는 두 가지 기본 성질로 설명됩니다.
유일성(Uniqueness)은 키 값으로 릴레이션 내의 모든 튜플을 유일하게 구분할 수 있는 성질입니다. 동일한 키 값을 가진 두 개의 튜플이 존재할 수 없습니다. 예를 들어, 학번은 한 학생에게 고유하므로 유일성을 만족합니다.
최소성(Minimality)은 키를 구성하는 속성 중 하나라도 제거하면 유일하게 식별할 수 없게 되는 성질입니다. 즉, 유일성을 만족시키기 위한 최소한의 속성만으로 키가 구성되어야 합니다. {학번, 이름}은 학번만으로도 유일하게 식별할 수 있으므로 최소성을 만족하지 않습니다.
학생 릴레이션: {학번, 이름, 학과, 이메일}
{학번} → 유일성 ✓ 최소성 ✓ → 후보키
{이메일} → 유일성 ✓ 최소성 ✓ → 후보키
{학번, 이름} → 유일성 ✓ 최소성 ✗ → 슈퍼키 (이름 제거해도 유일)
{학번, 이름, 학과} → 유일성 ✓ 최소성 ✗ → 슈퍼키
{이름} → 유일성 ✗ → 키 아님 (동명이인 가능)이 두 성질을 기준으로 키의 종류가 분류됩니다. 유일성만 만족하면 슈퍼키, 유일성과 최소성을 모두 만족하면 후보키입니다.
키의 계층 구조
키는 포함 관계에 따라 계층적으로 분류됩니다. 가장 넓은 범위의 슈퍼키에서 시작하여, 조건을 추가할수록 범위가 좁아집니다.
슈퍼키 (Super Key)
│ 최소성을 만족하면
▼
후보키 (Candidate Key)
│ 하나를 선택하면
├──→ 기본키 (Primary Key) ← 선택된 것
└──→ 대체키 (Alternate Key) ← 선택되지 않은 것
다른 테이블의 기본키를 참조하면
→ 외래키 (Foreign Key)이 계층 구조를 하나씩 상세히 살펴보겠습니다.
슈퍼키 (Super Key)
슈퍼키(Super Key)는 릴레이션에서 튜플을 유일하게 식별할 수 있는 속성(또는 속성들의 집합)입니다. 유일성만 만족하면 되고, 최소성은 요구하지 않습니다. 따라서 슈퍼키에 아무 속성이나 추가해도 여전히 슈퍼키입니다.
학생(학번, 이름, 학과, 이메일)
슈퍼키 목록
{학번} ← 유일 식별 가능
{이메일} ← 유일 식별 가능
{학번, 이름} ← 학번만으로도 유일 (과잉 속성 포함)
{학번, 학과} ← 학번만으로도 유일 (과잉 속성 포함)
{학번, 이름, 학과} ← 과잉 속성 더 많이 포함
{학번, 이름, 학과, 이메일} ← 모든 속성 (항상 슈퍼키)
{이메일, 이름} ← 이메일만으로도 유일 (과잉 속성 포함)
슈퍼키가 아닌 것
{이름} ← 동명이인 가능
{학과} ← 같은 학과 여러 학생
{이름, 학과} ← 같은 학과의 동명이인슈퍼키의 핵심 특성은 유일성은 보장하지만 불필요한 속성을 포함할 수 있다는 것입니다. 릴레이션의 모든 속성을 포함하는 집합은 항상 슈퍼키입니다. 왜냐하면 릴레이션의 정의상 중복 튜플이 존재하지 않으므로, 모든 속성의 조합은 반드시 유일하기 때문입니다.
후보키 (Candidate Key)
후보키(Candidate Key)는 슈퍼키 중에서 최소성을 만족하는 것입니다. 즉, 유일성과 최소성을 모두 만족하는 키입니다. "더 이상 속성을 빼면 유일하게 식별할 수 없는" 최소한의 속성 집합입니다.
학생(학번, 이름, 학과, 이메일)
{학번} → 슈퍼키인가? ✓ (학번으로 유일 식별 가능)
→ 최소인가? ✓ (속성이 하나이므로 더 줄일 수 없음)
→ 후보키 ✓
{이메일} → 슈퍼키인가? ✓ (이메일로 유일 식별 가능)
→ 최소인가? ✓ (속성이 하나)
→ 후보키 ✓
{학번, 이름} → 슈퍼키인가? ✓
→ 최소인가? ✗ (학번만으로도 유일 식별 가능)
→ 후보키 ✗ (슈퍼키일 뿐)한 릴레이션에 후보키는 하나 이상 존재합니다. 위 예시에서는 {학번}과 {이메일}이 모두 후보키입니다. 후보키가 여러 개 있을 때, 이 중 하나를 기본키로 선택합니다.
후보키를 찾는 실전적인 방법은 함수적 종속성(Functional Dependency)을 분석하는 것입니다. 속성 X가 속성 Y를 함수적으로 결정하면(X→Y), X는 Y를 유일하게 식별합니다. 릴레이션의 모든 속성을 함수적으로 결정하는 최소한의 속성 집합이 후보키입니다.
기본키 (Primary Key)
기본키(Primary Key, PK)는 후보키 중에서 릴레이션의 대표 식별자로 선택된 하나의 키입니다. 관계형 데이터베이스에서 가장 중요한 키이며, 모든 테이블에 반드시 하나 존재해야 합니다.
기본키의 특성은 다음과 같습니다.
- 유일성: 같은 기본키 값을 가진 튜플이 두 개 이상 존재할 수 없습니다.
- NOT NULL: 기본키는 NULL 값을 가질 수 없습니다. 이것은 개체 무결성 제약(Entity Integrity Constraint)이라 하며, 관계형 모델의 핵심 규칙입니다.
- 불변성: 기본키 값은 가급적 변경되지 않아야 합니다. 기본키가 변경되면 이를 참조하는 모든 외래키도 갱신해야 하기 때문입니다.
- 최소성: 후보키에서 선택되었으므로 최소성을 자동으로 만족합니다.
-- 컬럼 레벨 제약
CREATE TABLE students (
student_id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
department VARCHAR(50)
);
-- 테이블 레벨 제약 (제약 이름 부여 가능)
CREATE TABLE students (
student_id INT NOT NULL,
name VARCHAR(50) NOT NULL,
department VARCHAR(50),
CONSTRAINT pk_students PRIMARY KEY (student_id)
);기본키를 선택할 때의 실무 가이드라인은 다음과 같습니다.
- 단순할수록 좋습니다: 컬럼 수가 적고, 데이터 타입이 단순한 것을 선택합니다. 정수형이 문자열보다 비교 연산이 빠릅니다.
- 변경 가능성이 낮은 것을 선택합니다. 이메일은 변경될 수 있지만, AUTO_INCREMENT 값은 변경되지 않습니다.
- 의미 있는 데이터를 키로 사용하지 않는 것이 좋습니다. 비즈니스 규칙이 바뀌면 키 구조도 바뀌어야 하기 때문입니다.
- NULL 가능성이 있는 속성은 기본키로 사용할 수 없습니다.
대체키 (Alternate Key)
대체키(Alternate Key)는 후보키 중에서 기본키로 선택되지 않은 나머지 키입니다. 대체키도 유일성과 최소성을 만족하지만, 대표 식별자의 역할은 하지 않습니다.
학생(학번, 이름, 학과, 이메일)
후보키: {학번}, {이메일}
기본키로 {학번}을 선택하면:
기본키 = {학번}
대체키 = {이메일}
기본키로 {이메일}을 선택하면:
기본키 = {이메일}
대체키 = {학번}대체키는 SQL에서 UNIQUE 제약 조건으로 구현합니다. UNIQUE 제약은 해당 컬럼의 값이 중복되지 않도록 보장합니다.
CREATE TABLE students (
student_id INT PRIMARY KEY, -- 기본키
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE, -- 대체키 (UNIQUE)
department VARCHAR(50)
);기본키와 UNIQUE의 차이점이 있습니다. 기본키는 테이블당 하나만 선언할 수 있지만, UNIQUE 제약은 여러 개 선언할 수 있습니다. 또한 기본키는 NULL을 허용하지 않지만, UNIQUE 제약은 표준 SQL에서 NULL을 허용합니다(단, DBMS에 따라 차이가 있습니다).
외래키 (Foreign Key)
외래키(Foreign Key, FK)는 다른 릴레이션의 기본키를 참조하는 속성(또는 속성 집합)입니다. 외래키는 릴레이션 간의 관계를 표현하며, 참조 무결성(Referential Integrity)을 보장하는 메커니즘입니다.
외래키의 핵심 규칙은 다음과 같습니다.
- 외래키 값은 참조되는 릴레이션의 기본키 값 중 하나이거나 NULL이어야 합니다. 존재하지 않는 기본키를 참조할 수 없습니다.
- 이 규칙을 참조 무결성 제약(Referential Integrity Constraint)이라 합니다.
CREATE TABLE departments (
dept_id INT PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL
);
CREATE TABLE students (
student_id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);
-- 정상: departments에 dept_id=1이 존재
INSERT INTO students VALUES (1, '김철수', 1); -- ✓
-- 오류: departments에 dept_id=99가 없음
INSERT INTO students VALUES (2, '이영희', 99); -- ✗ 참조 무결성 위반
-- 정상: NULL은 허용 (미배정 학생)
INSERT INTO students VALUES (3, '박민수', NULL); -- ✓참조 무결성 옵션
외래키로 참조되는 부모 테이블의 행이 삭제되거나 기본키가 수정될 때 어떻게 처리할지를 결정하는 것이 참조 동작(Referential Action)입니다.
| 동작 | DELETE 시 | UPDATE 시 | 설명 |
|---|---|---|---|
| RESTRICT | 삭제 거부 | 수정 거부 | 참조하는 행이 있으면 거부 |
| CASCADE | 함께 삭제 | 함께 수정 | 부모가 바뀌면 자식도 따라감 |
| SET NULL | NULL로 설정 | NULL로 설정 | 부모가 사라지면 NULL |
| SET DEFAULT | 기본값으로 | 기본값으로 | 부모가 사라지면 기본값 |
| NO ACTION | SQL 표준의 RESTRICT | 실질적 동일 | 트랜잭션 끝에 검사 |
-- CASCADE: 학과가 삭제되면 소속 학생도 삭제
CREATE TABLE students (
student_id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
-- SET NULL: 학과가 삭제되면 학생의 dept_id를 NULL로
CREATE TABLE students (
student_id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
ON DELETE SET NULL
ON UPDATE CASCADE
);
-- RESTRICT: 학생이 소속된 학과는 삭제 불가
CREATE TABLE students (
student_id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
ON DELETE RESTRICT
);실무에서 가장 자주 사용하는 조합은 ON DELETE RESTRICT ON UPDATE CASCADE입니다. 부모 행이 실수로 삭제되는 것을 방지하면서도, 기본키 값이 변경될 때는 자동으로 반영되게 합니다.
자기 참조 외래키
외래키가 같은 테이블의 기본키를 참조하는 경우를 자기 참조(Self-Referencing)라 합니다. 조직의 계층 구조, 카테고리의 상-하위 관계 등을 표현할 때 사용합니다.
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
manager_id INT,
FOREIGN KEY (manager_id) REFERENCES employees(emp_id)
);
-- CEO는 상사가 없으므로 NULL
INSERT INTO employees VALUES (1, '대표이사', NULL);
-- 부장의 상사는 대표이사
INSERT INTO employees VALUES (2, '영업부장', 1);
-- 팀장의 상사는 부장
INSERT INTO employees VALUES (3, '영업1팀장', 2);자연키 vs 대리키
키를 선택할 때 실무에서 가장 자주 논의되는 주제가 자연키(Natural Key)와 대리키(Surrogate Key) 중 무엇을 기본키로 사용할 것인가입니다.
자연키(Natural Key)는 업무적 의미를 가진 실세계의 데이터를 키로 사용하는 것입니다. 주민등록번호, 이메일 주소, ISBN, 사업자등록번호 등이 자연키의 예입니다 .
대리키(Surrogate Key)는 시스템이 자동으로 생성하는 인공적인 키입니다. AUTO_INCREMENT, SEQUENCE, UUID 등으로 생성됩니다. 실세계에서는 아무 의미가 없고, 오로지 식별 목적으로만 사용됩니다.
| 구분 | 자연키 (Natural Key) | 대리키 (Surrogate Key) |
|---|---|---|
| 정의 | 실세계 의미를 가진 키 | 시스템이 생성한 인공 키 |
| 예시 | 주민등록번호, 이메일, ISBN | AUTO_INCREMENT, SEQUENCE, UUID |
| 장점 | 직관적, 의미 있음, 별도 생성 불필요 | 불변, 단순, 짧음, 성능 우수 |
| 단점 | 변경 가능, 민감 정보, 길 수 있음 | 의미 없음, 별도 생성 필요 |
| 실무 | 보조 유니크 제약으로 사용 | 기본키로 사용 (대부분) |
| JOIN 성능 | 문자열 비교 (느림) | 정수 비교 (빠름) |
| 프라이버시 | 개인정보 노출 위험 | 안전 |
실무에서 대리키를 기본키로 사용하는 이유를 구체적으로 살펴보겠습니다.
-- 자연키(이메일)를 기본키로 사용한 경우
CREATE TABLE users (
email VARCHAR(100) PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_email VARCHAR(100),
FOREIGN KEY (user_email) REFERENCES users(email)
);
-- 사용자가 이메일을 변경하고 싶다면?
-- users 테이블뿐 아니라 orders 테이블의 모든 행도 수정해야 합니다.
-- 외래키로 참조하는 테이블이 10개라면 10개 모두 수정!
UPDATE users SET email = 'new@example.com' WHERE email = 'old@example.com';
-- → orders 테이블도 갱신 필요 (CASCADE라면 자동, 아니면 수동)-- 대리키(user_id)를 기본키로 사용한 경우
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(100) NOT NULL UNIQUE, -- 자연키는 UNIQUE로
name VARCHAR(50)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
-- 이메일 변경이 아무리 자주 일어나도
-- 기본키(user_id)는 불변이므로 외래키에 영향 없음
UPDATE users SET email = 'new@example.com' WHERE user_id = 1;
-- → orders 테이블은 전혀 영향 없음!결론: 대리키를 기본키로 사용하고, 자연키에는 UNIQUE 제약을 거는 것이 안전한 설계입니다. 주민등록번호처럼 법적 제약으로 저장 자체가 제한되는 데이터는 더더욱 기본키로 사용해서는 안 됩니다.
복합키 (Composite Key)
복합키(Composite Key)는 두 개 이상의 속성을 조합하여 하나의 키로 사용하는 것입니다. 단일 속성으로는 유일하게 식별할 수 없지만, 여러 속성을 조합하면 유일하게 식별할 수 있을 때 사용합니다.
-- 수강 테이블: 학번 + 과목코드 조합이 유일
CREATE TABLE enrollments (
student_id NUMBER,
course_id NUMBER,
semester VARCHAR(10),
grade CHAR(2),
PRIMARY KEY (student_id, course_id, semester)
);
-- 학생 1이 과목 101을 2024년 1학기에 수강 → 유일한 조합
INSERT INTO enrollments VALUES (1, 101, '2024-1', 'A+');
-- 같은 학생이 같은 과목을 다른 학기에 재수강 → 허용
INSERT INTO enrollments VALUES (1, 101, '2024-2', 'B+');
-- 같은 학생, 같은 과목, 같은 학기 → 중복! 거부
INSERT INTO enrollments VALUES (1, 101, '2024-1', 'B0'); -- ✗ PK 위반M:N 관계의 중간 테이블(연결 테이블, 교차 테이블)에서 복합키를 자주 사용합니다. 주문-상품 관계의 주문상세 테이블, 학생-과목 관계의 수강 테이블이 대표적입니다.
복합키의 장단점
복합키는 별도의 대리키 없이 관계의 의미를 직접 표현할 수 있다는 장점이 있습니다. 그러나 실무에서는 몇 가지 단점이 있습니다.
-- 복합키를 외래키로 참조하려면 모든 구성 컬럼을 포함해야 함
CREATE TABLE grade_appeals (
appeal_id INT PRIMARY KEY,
student_id NUMBER,
course_id NUMBER,
semester VARCHAR(10),
reason TEXT,
-- 복합 외래키: 3개 컬럼 모두 필요
FOREIGN KEY (student_id, course_id, semester)
REFERENCES enrollments(student_id, course_id, semester)
);복합키를 구성하는 컬럼이 3개, 4개로 늘어나면 외래키 선언, JOIN 조건, WHERE 절이 모두 복잡해집니다. 이 때문에 실무에서는 복합키 대신 대리키를 기본키로 추가하고, 복합키는 UNIQUE 제약으로 처리하는 경우가 많습니다.
CREATE TABLE enrollments (
enrollment_id INT AUTO_INCREMENT PRIMARY KEY, -- 대리키
student_id NUMBER NOT NULL,
course_id NUMBER NOT NULL,
semester VARCHAR(10) NOT NULL,
grade CHAR(2),
UNIQUE (student_id, course_id, semester) -- 복합 유니크
);
-- 외래키 참조가 간단해짐
CREATE TABLE grade_appeals (
appeal_id INT PRIMARY KEY,
enrollment_id INT, -- 단일 컬럼 참조
reason TEXT,
FOREIGN KEY (enrollment_id) REFERENCES enrollments(enrollment_id)
);키 관련 SQL 함수와 전략
AUTO_INCREMENT와 SEQUENCE
대리키를 생성하는 가장 일반적인 방법입니다.
CREATE TABLE products (
product_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- product_id를 명시하지 않아도 자동 증가
INSERT INTO products (name) VALUES ('노트북'); -- product_id = 1
INSERT INTO products (name) VALUES ('키보드'); -- product_id = 2
INSERT INTO products (name) VALUES ('마우스'); -- product_id = 3CREATE SEQUENCE seq_product_id
START WITH 1
INCREMENT BY 1
NOCACHE;
CREATE TABLE products (
product_id NUMBER PRIMARY KEY,
name VARCHAR2(100) NOT NULL
);
INSERT INTO products VALUES (seq_product_id.NEXTVAL, '노트북');
INSERT INTO products VALUES (seq_product_id.NEXTVAL, '키보드');UUID
분산 시스템에서 중복 없는 식별자가 필요할 때 UUID(Universally Unique Identifier)를 사용합니다. 128비트의 고유한 값으로, 서로 다른 서버에서 동시에 생성해도 충돌 확률이 극히 낮습니다.
-- MySQL 8.0+
CREATE TABLE sessions (
session_id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
user_id INT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- UUID 예시: '550e8400-e29b-41d4-a716-446655440000'UUID는 분산 환경에서는 장점이 크지만, 단점도 있습니다. 36바이트(CHAR)로 저장 공간이 크고, 순차적이지 않아 B-Tree 인덱스 성능이 떨어질 수 있으며, 사람이 읽기 어렵습니다. MySQL 8.0에서는 UUID_TO_BIN()과 BIN_TO_UUID() 함수로 16바이트 BINARY로 변환하여 저장하는 것이 권장됩니다.
키와 인덱스의 관계
키와 인덱스는 밀접한 관계가 있지만 서로 다른 개념입니다.
키(Key)는 논리적 개념입니다. 데이터의 유일성과 관계를 정의하는 논리적 규칙입니다.
인덱스(Index)는 물리적 개념입니다. 데이터 검색 속도를 높이기 위한 자료구조(보통 B-Tree)입니다.
관계형 DBMS에서 기본키를 선언하면 자동으로 고유 인덱스(Unique Index)가 생성됩니다. UNIQUE 제약도 마찬가지입니다. 외래키에는 자동으로 인덱스가 생성되지 않는 DBMS가 많은데(MySQL InnoDB는 자동 생성), 외래키 컬럼에 인덱스가 없으면 JOIN 성능이 크게 저하될 수 있으므로 직접 인덱스를 생성해주는 것이 좋습니다.
CREATE TABLE orders (
order_id INT PRIMARY KEY, -- PK → 자동으로 Unique Index 생성
user_id INT NOT NULL,
email VARCHAR(100) UNIQUE, -- UNIQUE → 자동으로 Unique Index 생성
ordered_at DATE,
FOREIGN KEY (user_id) REFERENCES users(user_id)
-- FK에는 인덱스가 자동 생성되지 않을 수 있음 (DBMS 의존)
);
-- 외래키 컬럼에 명시적 인덱스 추가 (JOIN 성능 향상)
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- 날짜 범위 조회가 빈번하면 인덱스 추가
CREATE INDEX idx_orders_date ON orders(ordered_at);키 선택의 실무 패턴
실무에서 키를 설정할 때 자주 사용하는 패턴을 정리합니다.
패턴 1: 대리키 + 자연키 UNIQUE
가장 일반적이고 안전한 패턴입니다.
CREATE TABLE users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
username VARCHAR(50) NOT NULL UNIQUE,
name VARCHAR(50) NOT NULL
);패턴 2: 복합 유니크 + 대리키
M:N 중간 테이블에서 주로 사용합니다.
CREATE TABLE user_roles (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
assigned_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);패턴 3: 시간 기반 정렬 ID
분산 시스템에서 시간순 정렬이 가능한 ID가 필요할 때 사용합니다. Twitter의 Snowflake, ULID(Universally Unique Lexicographically Sortable Identifier) 등이 이에 해당합니다.
ULID: 01ARZ3NDEKTSV4RRFFQ69G5FAV
┌───────────┬───────────────────┐
│ Timestamp │ Randomness │
│ (48bit) │ (80bit) │
│밀리초 정밀│ 암호학적 난수 │
└───────────┴───────────────────┘
장점: 시간순 정렬 가능 + UUID처럼 유일
단점: UUID보다 생태계가 작음키 관련 무결성 제약 정리
키는 무결성 제약과 직결됩니다. 전체 무결성 제약을 키 관점에서 정리합니다.
| 무결성 유형 | 관련 키 | 규칙 | SQL |
|---|---|---|---|
| 개체 무결성 | 기본키 | PK는 NULL 불가 | PRIMARY KEY |
| 참조 무결성 | 외래키 | FK 값은 참조 PK에 존재하거나 NULL | FOREIGN KEY ... REFERENCES |
| 도메인 무결성 | - | 속성 값은 도메인 범위 내 | CHECK, 데이터 타입 |
| 키 무결성 | 후보키 | 릴레이션에 최소 하나의 후보키 존재 | PRIMARY KEY, UNIQUE |
| NULL 무결성 | - | 특정 속성에 NULL 불허 | NOT NULL |
| 고유 무결성 | 대체키 | 특정 속성 값의 유일성 보장 | UNIQUE |
키 관련 흔한 실수
첫째, 자연키를 기본키로 사용하는 것입니다. 주민등록번호를 기본키로 쓰면 개인정보 보호법 위반 가능성이 있고, 이메일을 기본키로 쓰면 변경 시 대규모 갱신이 필요합니다.
둘째, 외래키를 SQL에서 선언하지 않는 것입니다. ORM을 사용하면 애플리케이션 코드로만 관계를 관리하고 DB에 FK를 선언하지 않는 경우가 있습니다. 이 경우 애플리케이션 바깥(직접 SQL 실행, 배치 작업 등)에서 무결성이 깨질 수 있습니다.
셋째, 복합키의 컬럼 순서를 고려하지 않는 것입니다. PRIMARY KEY (a, b)에서 인덱스는 (a, b) 순서로 생성됩니다. WHERE a = 1은 인덱스를 탈 수 있지만, WHERE b = 2만으로는 인덱스를 타지 못합니다. 복합키의 컬럼 순서는 조회 패턴에 맞게 결정해야 합니다.
넷째, 모든 테이블에 id를 무조건 추가하는 것입니다. 대리키가 대부분의 경우 좋은 선택이지만, 코드 테이블(성별: M/F, 상태: ACTIVE/INACTIVE)처럼 데이터가 소량이고 코드 자체가 의미 있는 경우에는 자연키가 더 적합할 수 있습니다.
키는 관계형 데이터베이스의 정체성을 결정짓는 핵심 개념입니다. 올바른 키 설계는 데이터 무결성의 기반이 되고, 테이블 간의 관계를 명확히 표현하며, 조회 성능에도 직접적인 영향을 미칩니다. 다음 절에서는 데이터의 정확성을 보장하는 무결성 제약을 다루겠습니다.