1. 조인 연산이란

조인(join)은 두 개의 릴레이션을 연결해서 하나의 결과 릴레이션으로 만드는 연산이다.
관계형 데이터베이스에서는 데이터가 보통 여러 테이블로 나뉘어 저장된다.
그래서 실제로 원하는 정보를 얻으려면 테이블을 합쳐서 봐야 하는 경우가 많다.
예를 들어
- student
- takes
이 두 테이블이 있다고 하면,
학생 이름과 수강한 과목을 함께 보고 싶을 때 조인을 쓴다.
조인은 카티션 곱과 비슷해 보이지만,
아무 튜플이나 다 합치는 게 아니라 특정 조건을 만족하는 튜플끼리만 결합한다.
실제 SQL에서는 보통 FROM 절에서 쓴다.
2. 조인 연산의 종류
조인은 크게 다음처럼 나눠서 보면 된다.
- 자연 조인(NATURAL JOIN)
- 내부 조인(INNER JOIN)
- 외부 조인(OUTER JOIN)
- LEFT OUTER JOIN
- RIGHT OUTER JOIN
- FULL OUTER JOIN
3. 자연 조인(NATURAL JOIN)
자연 조인은 두 릴레이션에서 이름이 같은 모든 속성을 기준으로 자동으로 매칭해서 조인한다.
즉, 공통 이름의 열이 있으면
그 열의 값이 같은 튜플끼리 자동으로 합쳐진다.
특징
- 공통 속성을 자동으로 찾아서 조인
- 결과에서 공통 속성은 한 번만 나타남
- 결과는 하나의 릴레이션
예를 들면:
FROM student NATURAL JOIN takes;
이 경우 두 테이블에서 이름이 같은 속성을 전부 기준으로 조인한다.
자연 조인의 장점
- 문법이 짧다
- 공통 속성이 명확할 때는 편하다
자연 조인의 위험성
문제는 이름이 같다고 해서 항상 같은 의미의 속성은 아니라는 점이다.
예를 들어:
- course.dept_name
- student.dept_name
이 둘은 이름은 같아도 의미가 다를 수 있다.
그런데 NATURAL JOIN을 쓰면 이 둘도 자동으로 매칭해버릴 수 있다.
이게 자연 조인의 가장 큰 위험이다.
즉, 편하긴 한데 의도치 않은 조인 조건이 숨어들 수 있다.
4. USING과 ON 차이
자연 조인 대신 더 안전하게 조인하려면 USING 이나 ON 을 많이 쓴다.
USING
두 릴레이션에 이름이 같은 속성이 있을 때,
그중 어떤 공통 속성으로 조인할지 명시하는 방식이다.
FROM student JOIN takes USING (course_id);
특징
- 공통 속성 이름이 같아야 사용 가능
- 결과에서 공통 속성은 한 번만 표시됨
- 문법이 간단함
ON
조인 조건을 직접 적는 방식이다.
FROM student JOIN takes
ON student.ID = takes.ID;
특징
- 속성 이름이 달라도 사용 가능
- 가장 일반적이고 유연함
- 결과에서 양쪽 속성이 모두 남을 수 있음
USING vs ON 한 번에 정리
| 사용 조건 | 같은 이름 속성이 있어야 함 | 이름 달라도 가능 |
| 조건 방식 | USING(column) | ON A.col = B.col |
| 결과 컬럼 | 공통 컬럼 1번만 표시 | 양쪽 컬럼 다 남을 수 있음 |
| 장점 | 짧고 깔끔 | 유연하고 명확 |
| 실무 사용 | 제한적 | 훨씬 많이 사용 |
실제로는 ON을 가장 많이 쓴다고 보면 된다.
왜냐면 조건을 명시적으로 보여줘서 실수할 가능성이 적기 때문이다.
5. 내부 조인(INNER JOIN)
내부 조인은 조건에 맞는 튜플만 결과에 포함하는 조인이다.
사실 JOIN 이라고만 쓰면 대부분 INNER JOIN 의미다.
FROM student
JOIN takes
ON student.ID = takes.ID;
이 경우 양쪽 테이블에서 ID가 일치하는 행만 결과로 나온다.
즉, 매칭되지 않는 튜플은 버려진다.
6. 외부 조인(OUTER JOIN)
외부 조인은 매칭되지 않는 튜플도 결과에 보존하는 조인이다.
일치하지 않는 쪽은 NULL로 채운다.
이게 내부 조인과 가장 큰 차이다.
LEFT OUTER JOIN
왼쪽 릴레이션의 모든 튜플을 유지한다.
FROM student
LEFT OUTER JOIN takes
ON student.ID = takes.ID;
이 경우 수강 정보가 없는 학생도 결과에 나타난다.
단, takes 쪽 컬럼은 NULL이 된다.
RIGHT OUTER JOIN
오른쪽 릴레이션의 모든 튜플을 유지한다.
FULL OUTER JOIN
양쪽 릴레이션에서 매칭되지 않은 튜플도 전부 보존한다.
즉,
- 왼쪽에만 있는 것
- 오른쪽에만 있는 것
둘 다 결과에 남는다.
LEFT OUTER JOIN vs NATURAL LEFT OUTER JOIN
| 조인 조건 | ON 또는 USING으로 직접 지정 | 같은 이름 속성 자동 매칭 |
| 결과 컬럼 | 중복 속성 남을 수 있음 | 공통 속성 하나로 합쳐짐 |
| 제어 가능성 | 높음 | 낮음 |
| 안전성 | 더 안전 | 자동 매칭이라 주의 필요 |
정리하면,
NATURAL JOIN 계열은 편하지만 위험하고,
보통은 LEFT OUTER JOIN ... ON ... 형태를 더 많이 쓴다.
7. 조인 종류와 조인 조건은 다르다
이건 은근 많이 헷갈린다.
조인 종류(Join Type)
매칭되지 않는 튜플을 어떻게 처리할지 결정한다.
예:
- INNER
- LEFT OUTER
- RIGHT OUTER
- FULL OUTER
조인 조건(Join Condition)
어떤 튜플끼리 매칭할지 결정한다.
예:
- NATURAL
- USING
- ON
즉,
- 종류 = 안 맞는 행을 버릴지 살릴지
- 조건 = 어떤 기준으로 연결할지
라고 이해하면 된다.
8. 뷰(View)란?

뷰는 기본 테이블이나 다른 뷰로부터 파생된 가상 릴레이션이다.
쉽게 말하면,
복잡한 SELECT 문에 이름을 붙여서 테이블처럼 사용하는 것이다.
뷰의 특징
- 실제 데이터를 따로 저장하지 않음
- 질의 시점에 기본 테이블을 바탕으로 계산됨
- 복잡한 질의를 단순화할 수 있음
- 보안 목적으로도 유용함
예:
SELECT *
FROM instructor
WHERE dept_name = 'Geology';
이제 geo_instructor를 테이블처럼 사용할 수 있다.
9. 뷰 정의할 때 중요한 점
뷰를 정의한다고 해서 결과가 즉시 테이블처럼 저장되는 건 아니다.
이거 진짜 중요하다.
즉,
는 “결과를 저장”하는 게 아니라
SQL 표현식을 저장하는 것에 가깝다.
필요할 때마다 그 정의를 이용해서 결과를 계산한다.
뷰 제거
10. 뷰의 장점
1) 복잡한 질의 단순화
긴 조인 쿼리를 뷰로 만들어 두면 이후엔 짧게 쓸 수 있다.
2) 보안
사용자에게 전체 테이블을 공개하지 않고 필요한 열/행만 보이게 할 수 있다.
3) 논리적 독립성
사용자 입장에서는 복잡한 내부 구조를 몰라도 된다.
11. 갱신 가능한 뷰 / 불가능한 뷰
모든 뷰가 INSERT, UPDATE, DELETE 가능한 건 아니다.
일반적으로 갱신 불가능한 뷰
다음 요소가 들어가면 보통 갱신이 어렵다.
- 집계 함수 (AVG, SUM 등)
- GROUP BY
- DISTINCT
- 여러 테이블 조인
- 계산식이 많이 포함된 경우
예를 들어 평균 급여를 보여주는 뷰에 값을 삽입하는 건 애초에 의미가 모호하다.
갱신 가능한 뷰의 조건
보통 다음 조건을 만족해야 비교적 갱신 가능하다.
- SELECT 절에 단순한 속성 이름만 있음
- FROM 절에 릴레이션 하나만 있음
- GROUP BY, HAVING 없음
- 생략된 속성이 NULL 허용 또는 DEFAULT 가능해야 함
즉, “거의 기본 테이블처럼 보이는 단순 뷰”여야 한다.
12. WITH CHECK OPTION
이 옵션은 뷰를 통해 삽입/수정되는 데이터가
그 뷰의 조건을 반드시 만족하도록 강제한다.
예:
SELECT *
FROM instructor
WHERE dept_name = 'Comp. Sci.'
WITH CHECK OPTION;
이제 이 뷰를 통해 데이터를 넣거나 수정할 때는
반드시 dept_name = 'Comp. Sci.' 조건을 만족해야 한다.
이 옵션이 없으면
뷰를 통해 수정한 결과가 오히려 뷰에서 안 보이는 이상한 상황이 생길 수 있다.
13. 뷰 갱신에서 생기는 문제
예를 들어 이런 뷰가 있다고 하자.
SELECT id, name, building
FROM instructor I, department D
WHERE I.dept_name = D.dept_name;
여기에
를 하면 문제가 생긴다.
왜냐면 뷰에는 dept_name이 없어서,
실제 기본 테이블에 어떻게 넣어야 할지 정보가 부족하기 때문이다.
발생 가능한 문제
- 삽입된 튜플이 조인 조건을 만족하지 않아 뷰 결과에 안 보일 수 있음
- 기본 테이블의 필수 속성이나 기본키가 NULL이 되어 삽입 불가능할 수 있음
즉, 조인 뷰는 갱신이 특히 까다롭다.
14. 실체화 뷰(Materialized View)
일반 뷰는 결과를 매번 계산하지만,
실체화 뷰는 결과를 실제로 저장한다.
장점
- 조회 속도가 빠름
- 복잡한 집계나 분석 쿼리에 유리
단점
- 저장 공간 필요
- 원본 데이터 변경 시 최신 상태 유지 필요
- 갱신 비용 발생
즉, 속도와 저장/유지 비용의 트레이드오프가 있다.
15. 무결성 제약조건이란
무결성 제약조건은 잘못된 데이터가 들어오는 걸 막는 규칙이다.
데이터베이스는 한 번 이상한 값이 들어가면
그 뒤로 모든 질의 결과가 오염될 수 있다.
그래서 제약조건은 진짜 중요하다.
16. 단일 릴레이션 제약조건
NOT NULL
해당 속성에 NULL을 허용하지 않는다.
PRIMARY KEY
기본키를 지정한다.
자동으로 유일성과 NOT NULL 성질을 가진다.
UNIQUE
중복을 허용하지 않는다.
다만 SQL에서는 NULL 처리 방식이 DBMS마다 약간 다를 수 있는데,
보통 여러 NULL을 허용하는 경우가 많다.
CHECK (조건)
속성 값 범위를 제한한다.
예:
semester varchar(6),
CHECK (semester IN ('Fall','Winter','Spring','Summer'))
);
또는
이런 식으로 사용한다.
17. 참조 무결성 제약조건
참조 무결성은 외래키 값이 반드시 다른 릴레이션의 기본키와 일치해야 한다는 규칙이다.
예:
dept_name varchar(20),
FOREIGN KEY (dept_name) REFERENCES department(dept_name)
);
이제 instructor.dept_name에는
반드시 department에 존재하는 학과명만 들어갈 수 있다.
외래키 옵션 정리
| ON DELETE CASCADE | 부모 삭제 시 자식도 같이 삭제 |
| ON DELETE SET NULL | 부모 삭제 시 자식 외래키를 NULL로 변경 |
| ON DELETE SET DEFAULT | 부모 삭제 시 자식 외래키를 기본값으로 변경 |
| ON UPDATE CASCADE | 부모 값 변경 시 자식도 자동 반영 |
| ON UPDATE NO ACTION | 부모 값 변경을 막음 |
실무에서는 특히 CASCADE를 쓸 때 조심해야 한다.
잘못하면 연쇄 삭제가 발생할 수 있다.
18. SQL 날짜/시간 타입
SQL에는 날짜와 시간을 다루는 타입도 있다.
| DATE | '2005-07-27' | 연/월/일 |
| TIME | '09:00:30.75' | 시/분/초 |
| TIMESTAMP | '2005-07-27 09:00:30.75' | 날짜 + 시간 |
| INTERVAL | '1 day' | 시간 간격 |
이 타입들은 예약 시스템, 로그 저장, 기간 계산 같은 데서 많이 쓴다.
19. 대형 객체 타입 (LOB)
대용량 데이터를 저장할 때는 LOB 타입을 쓴다.
BLOB
- Binary Large Object
- 이미지, 영상, 바이너리 데이터 저장
CLOB
- Character Large Object
- 문서, 긴 텍스트 저장
실제로는 대형 데이터를 DB 자체에 전부 넣기보다
참조 포인터나 파일 경로처럼 관리하는 방식도 많이 쓴다.
20. 사용자 정의 타입과 도메인
CREATE TYPE
사용자 정의 복합 타입을 만들 수 있다.
예:
dollars numeric(12,2)
);
이후 테이블에서 사용할 수 있다.
dept_name varchar(20),
budget Dollars
);
CREATE DOMAIN
도메인은 기존 타입에 제약조건을 붙인 재사용 가능한 타입이라고 생각하면 된다.
예:
CONSTRAINT degree_level_test
CHECK (VALUE IN ('Bachelors', 'Masters', 'Doctorate'));
이렇게 만들어두면 여러 테이블에서 일관되게 재사용할 수 있다.
타입 vs 도메인 차이
- TYPE: 구조 자체를 새로 정의
- DOMAIN: 기존 타입 + 제약조건 재사용
이렇게 구분하면 된다.
21. 인덱스(Index)란
인덱스는 검색을 빠르게 하기 위한 자료구조다.
쉽게 말하면 책 뒤의 색인 같은 느낌이다.
본문을 처음부터 끝까지 다 안 읽고도 원하는 내용을 빨리 찾을 수 있게 해준다.
인덱스의 핵심
- 속성값과 해당 튜플 위치를 저장
- 특정 조건 검색 속도 향상
- 한 번 만들면 같은 조건의 질의에 반복적으로 도움됨
예:
ON instructor(dept_name, salary);
왜 그냥 테이블을 정렬해두면 안 될까?
이 질문도 자주 나온다.
예를 들어 테이블을 dept_name 기준으로 정렬하면
다른 속성 기준 정렬은 깨진다.
즉,
- 하나의 정렬 상태로 모든 검색을 빠르게 만들 수는 없음
- 그래서 검색이 자주 발생하는 속성마다 인덱스를 따로 둔다
인덱스의 단점도 있다
- 저장 공간 추가 필요
- INSERT, UPDATE, DELETE 때 인덱스도 함께 갱신해야 해서 쓰기 비용 증가
즉, 조회는 빨라지지만 갱신은 더 무거워질 수 있다.
22. 권한(Authorization)
DB에서는 모든 사용자가 모든 데이터를 다 만지면 안 된다.
그래서 권한 관리가 필요하다.
기본 권한 종류
| SELECT | 조회 |
| INSERT | 삽입 |
| UPDATE | 수정 |
| DELETE | 삭제 |
| ALL PRIVILEGES | 모든 권한 |
예:
GRANT UPDATE (budget) ON department TO amit, satoshi;
특정 컬럼에 대해서만 권한을 줄 수도 있다.
권한 회수는 이렇게 한다.
REVOKE UPDATE (budget) ON department FROM amit, satoshi;
23. ROLE이란
ROLE은 권한 묶음이라고 보면 된다.
사용자마다 권한을 하나하나 주는 대신,
역할을 만들고 거기에 권한을 준 다음 사용자를 그 역할에 넣는 방식이다.
예:
GRANT instructor TO amit;
CREATE ROLE teaching_assistant;
GRANT teaching_assistant TO instructor;
GRANT SELECT ON takes TO instructor;
이렇게 하면 권한 관리가 훨씬 편해진다.
24. 뷰에 대한 권한
뷰는 보안 목적으로 정말 유용하다.
예:
SELECT * FROM instructor WHERE dept_name = 'Geology';
GRANT SELECT ON geo_instructor TO geo_staff;
이 경우 geo_staff는 원본 instructor 전체를 볼 필요 없이
뷰를 통해 필요한 데이터만 조회할 수 있다.
중요한 포인트
- 뷰를 만든 사용자는 원본 테이블 접근 권한이 있어야 함
- 뷰를 사용하는 사람은 원본 테이블 권한이 없어도,
뷰에 대한 권한만 있으면 사용할 수 있는 경우가 많음
즉, 뷰는 보안 필터 역할도 할 수 있다.
25. USAGE, SELECT, CREATE 헷갈리는 이유 정리
이 부분은 진짜 많이 헷갈린다.
권한은 대상 객체가 무엇인지에 따라 이름이 다르다.
| TABLE / VIEW | SELECT, INSERT, UPDATE, DELETE |
| SCHEMA | USAGE, CREATE |
| DATABASE | CONNECT, CREATE, TEMP |
| FUNCTION / SEQUENCE | EXECUTE, USAGE |
GRANT SELECT ON mini_user TO maker;
- 대상: 테이블
- 의미: mini_user 데이터를 조회할 수 있음
GRANT USAGE ON SCHEMA public TO maker;
- 대상: 스키마
- 의미: public이라는 공간 안의 객체를 인식하고 접근할 수 있음
비유하면 이렇다.
- 스키마 = 폴더
- 테이블 = 파일
- USAGE = 폴더에 들어갈 권한
- SELECT = 파일 내용 읽기 권한
- CREATE = 폴더 안에 새 파일 만들기 권한
즉, 어떤 사용자가 public 안에서 뷰를 만들고 테이블을 읽으려면 보통
- USAGE ON SCHEMA
- CREATE ON SCHEMA
- 테이블에 대한 SELECT
이 조합이 필요하다.
26. 핵심 요약
이번 4장의 핵심만 다시 정리하면 이렇다.
- 조인은 두 릴레이션을 조건에 따라 결합하는 연산이다.
- NATURAL JOIN은 편하지만, 동명이 속성을 자동 매칭해서 위험할 수 있다.
- 실무에서는 보통 JOIN ... ON ... 을 더 많이 쓴다.
- INNER JOIN은 매칭된 튜플만, OUTER JOIN은 매칭되지 않은 튜플도 보존한다.
- 뷰(View) 는 가상 릴레이션으로, 복잡한 질의 단순화와 보안에 유용하다.
- 모든 뷰가 갱신 가능한 건 아니며, 특히 조인/집계 뷰는 갱신이 어렵다.
- WITH CHECK OPTION은 뷰 조건을 만족하는 데이터만 삽입/수정하게 한다.
- 제약조건에는 NOT NULL, PRIMARY KEY, UNIQUE, CHECK, FOREIGN KEY 등이 있다.
- 인덱스는 조회 속도를 높여주지만 갱신 비용도 늘린다.
- 권한 관리는 GRANT, REVOKE, ROLE을 중심으로 이해하면 된다.
마무리
4장은 내용이 많아서 처음 보면 좀 산만하게 느껴질 수 있는데, 큰 흐름은 이렇다.
- 조인으로 여러 테이블을 연결하고
- 뷰로 복잡한 질의를 감추고
- 제약조건으로 이상한 데이터 입력을 막고
- 인덱스로 조회 성능을 높이고
- 권한 관리로 보안을 지킨다
4장은 “데이터베이스를 실제로 운영하려면 필요한 기능들”을 한 번에 모아놓은 느낌이다.
여기까지 이해하면 이제 SQL이 단순 조회 언어가 아니라 구조, 성능, 무결성, 보안까지 다루는 언어라는 게 확실히 보이기 시작한다.