"many-to-many" 관계는 한 테이블의 한 레코드가 다른 테이블의 여러 레코드와 연결될 수 있고, 반대로도 가능한 거야. 예를 들어: - 한 학생(students 테이블에서)이 여러 과목(courses 테이블에서)을 수강할 수 있어. - 한 과목도 여러 학생이 들을 수 있지.
근데 이런 관계를 직접 테이블에 저장하려면 진짜 불편해. 그래서 중간 테이블이 등장해서 이 관계들을 저장해주는 거야.
실생활 예시
학생 테이블이랑 과목 테이블을 만들었다고 생각해봐. 만약 각 테이블에 모든 관련 데이터를 저장하려고 하면 완전 난장판이야:
students테이블에 학생이 듣는 모든 과목 리스트를 저장해야 해. 근데 리스트를 어떻게 저장하지? 콤마로 구분? 배열로? 쿼리 짜기 엄청 힘들어질 거야.courses테이블에 학생 리스트를 저장해야 하는데, 이건 진짜 머리 아파.
그래서 정답은 — 학생과 과목 사이의 관계를 저장하는 세 번째 테이블을 만드는 거야.
중간 테이블: 우리의 구세주!
중간 테이블(가끔 관계 테이블이라고도 해)은 모든 문제를 해결해줘. 이 테이블에는 두 개의 외래 키가 있어:
students테이블을 참조하는 외래 키.courses테이블을 참조하는 외래 키.
이 테이블의 각 레코드는 특정 학생과 특정 과목 사이의 관계를 만들어줘.
"many-to-many" 관계를 위한 테이블 만들기
이제 실습해보자! 학생과 과목 사이의 관계를 위한 테이블을 이렇게 만들 수 있어:
1단계: students 테이블 만들기
이게 학생 테이블이야. 여기서 학생의 고유 ID랑 이름을 저장해.
CREATE TABLE students (
student_id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
student_id— 학생의 고유 식별자야(SERIAL덕분에 자동 증가!).name— 학생 이름.
2단계: courses 테이블 만들기
이제 과목 테이블을 만들어보자. 여기서 과목의 고유 ID랑 이름을 저장해.
CREATE TABLE courses (
course_id SERIAL PRIMARY KEY,
title TEXT NOT NULL
);
course_id— 과목의 고유 식별자.title— 과목 이름.
3단계: 중간 테이블 enrollments 만들기
이제 우리의 마법 같은 관계 테이블을 만들어보자. 이 테이블에는 두 개의 컬럼이 있는데, 각각은 해당 테이블을 참조하는 외래 키야.
CREATE TABLE enrollments (
student_id INT REFERENCES students(student_id),
course_id INT REFERENCES courses(course_id),
PRIMARY KEY (student_id, course_id)
);
구조를 살펴보면:
student_id—students테이블의student_id를 참조하는 외래 키.course_id—courses테이블의course_id를 참조하는 외래 키.PRIMARY KEY (student_id, course_id)— 두 외래 키 조합이 기본 키야. 이걸로 각 관계가 유일하게 보장돼.
데이터 삽입하기
이제 데이터 좀 넣어서 어떻게 돌아가는지 확인해보자.
1단계: 학생 추가하기
INSERT INTO students (name) VALUES
('Alice'),
('Bob'),
('Charlie');
결과:
| student_id | name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Charlie |
2단계: 과목 추가하기
INSERT INTO courses (title) VALUES
('Mathematics'),
('History'),
('Biology');
결과:
| course_id | title |
|---|---|
| 1 | Mathematics |
| 2 | History |
| 3 | Biology |
3단계: enrollments에 데이터 넣기
이제 학생들을 과목에 등록시켜보자. 예를 들어:
Alice는Mathematics랑History를 듣고 있어.Bob은Biology만 듣고 있어.Charlie는 세 과목 다 듣고 있어.
INSERT INTO enrollments (student_id, course_id) VALUES
(1, 1), -- Alice가 Mathematics 듣는 중
(1, 2), -- Alice가 History 듣는 중
(2, 3), -- Bob이 Biology 듣는 중
(3, 1), -- Charlie가 Mathematics 듣는 중
(3, 2), -- Charlie가 History 듣는 중
(3, 3); -- Charlie가 Biology 듣는 중
결과:
| student_id | course_id |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
"many-to-many" 관계를 위한 쿼리
이제 데이터가 있으니, 제대로 써먹어보자!
특정 학생이 듣는 모든 과목 찾기
예를 들어, Alice(ID = 1)가 듣는 과목을 알고 싶으면 이렇게 쿼리하면 돼:
SELECT c.title
FROM courses c
JOIN enrollments e ON c.course_id = e.course_id
WHERE e.student_id = 1;
결과:
| title |
|---|
| Mathematics |
| History |
특정 과목을 듣는 모든 학생 찾기
예를 들어, Mathematics(ID = 1)를 듣는 학생이 누군지 알고 싶으면:
SELECT s.name
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
WHERE e.course_id = 1;
결과:
| name |
|---|
| Alice |
| Charlie |
학생과 그가 듣는 과목 전체 보기
누가 어떤 과목을 듣는지 전체적으로 보고 싶으면 이렇게 쿼리해:
SELECT s.name AS student, c.title AS course
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN courses c ON e.course_id = c.course_id;
결과:
| student | course |
|---|---|
| Alice | Mathematics |
| Alice | History |
| Bob | Biology |
| Charlie | Mathematics |
| Charlie | History |
| Charlie | Biology |
enrollments 테이블 덕분에 스키마가 엄청 유연해져 — 학생과 과목 사이의 관계를 쉽게 추가하거나 삭제할 수 있고, 원본 테이블은 건드릴 필요 없어. JOIN 쿼리로 누가 어떤 과목을 듣는지 쉽게 찾을 수 있고, 외래 키가 있어서 실수로 존재하지 않는 과목에 학생을 등록하는 일도 막아줘.
"many-to-many" 관계에서 흔한 실수
유일성 제약 없음: PRIMARY KEY를 안 걸면 같은 관계가 여러 번 들어갈 수도 있어.
데이터 무결성 깨짐: 없는 student_id나 course_id로 레코드를 넣으려고 하면 에러 나.
삭제 순서 실수: courses에서 과목을 먼저 지우면 enrollments에 "고아" 레코드가 남아. 이걸 막으려면 외래 키 정의에 ON DELETE CASCADE를 써줘야 해.
GO TO FULL VERSION