4. SQL
4. SQL
SQL 개요
SQL(Structured Query Language)은 관계형 데이터베이스를 관리하고 조작하기 위한 표준 언어다.
SQL 분류
1
2
3
4
5
6
7
8
9
10
11
1. DDL (Data Definition Language) - 데이터 정의
CREATE, ALTER, DROP, TRUNCATE
2. DML (Data Manipulation Language) - 데이터 조작
SELECT, INSERT, UPDATE, DELETE
3. DCL (Data Control Language) - 데이터 제어
GRANT, REVOKE
4. TCL (Transaction Control Language) - 트랜잭션 제어
COMMIT, ROLLBACK, SAVEPOINT
DDL (데이터 정의어)
CREATE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 테이블 생성
CREATE TABLE Student (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
dept VARCHAR(30) DEFAULT 'Undeclared',
grade INT CHECK (grade BETWEEN 1 AND 4),
email VARCHAR(100) UNIQUE,
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES Department(id)
ON DELETE SET NULL
ON UPDATE CASCADE
);
-- 제약 조건 정리
-- PRIMARY KEY: 기본키 (NOT NULL + UNIQUE)
-- NOT NULL: NULL 불허
-- UNIQUE: 중복 불허 (NULL은 허용)
-- CHECK: 값 범위 제한
-- DEFAULT: 기본값
-- FOREIGN KEY: 외래키 참조
ALTER
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 컬럼 추가
ALTER TABLE Student ADD COLUMN phone VARCHAR(20);
-- 컬럼 삭제
ALTER TABLE Student DROP COLUMN phone;
-- 컬럼 수정
ALTER TABLE Student MODIFY COLUMN name VARCHAR(100) NOT NULL;
-- 제약 조건 추가
ALTER TABLE Student ADD CONSTRAINT chk_grade CHECK (grade >= 1);
-- 제약 조건 삭제
ALTER TABLE Student DROP CONSTRAINT chk_grade;
DROP / TRUNCATE
1
2
3
4
5
6
7
8
9
10
11
12
13
-- 테이블 삭제 (스키마 + 데이터 전체)
DROP TABLE Student;
-- 참조하는 FK가 있으면 CASCADE 필요
DROP TABLE Student CASCADE;
-- 데이터만 삭제 (스키마 유지, 롤백 불가)
TRUNCATE TABLE Student;
-- DROP vs TRUNCATE vs DELETE
-- DROP: 테이블 자체 삭제
-- TRUNCATE: 데이터만 삭제 (빠름, DDL, 롤백 불가)
-- DELETE: 데이터 삭제 (느림, DML, 롤백 가능, WHERE 사용 가능)
DML (데이터 조작어)
SELECT
1
2
3
4
5
6
7
8
9
10
11
-- 기본 구조
SELECT [DISTINCT] 컬럼1, 컬럼2, ...
FROM 테이블
[WHERE 조건]
[GROUP BY 컬럼]
[HAVING 그룹 조건]
[ORDER BY 컬럼 [ASC|DESC]]
[LIMIT n];
-- 실행 순서 (작성 순서와 다름!)
-- FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 기본 조회
SELECT * FROM Student;
SELECT name, dept FROM Student;
SELECT DISTINCT dept FROM Student;
-- 조건 조회
SELECT * FROM Student WHERE grade >= 3;
SELECT * FROM Student WHERE dept = 'CS' AND grade = 4;
SELECT * FROM Student WHERE dept IN ('CS', 'EE', 'ME');
SELECT * FROM Student WHERE name LIKE '김%'; -- 김으로 시작
SELECT * FROM Student WHERE email IS NULL;
SELECT * FROM Student WHERE grade BETWEEN 2 AND 4;
-- 정렬
SELECT * FROM Student ORDER BY grade DESC, name ASC;
-- 별칭 (Alias)
SELECT s.name AS student_name, d.name AS dept_name
FROM Student s, Department d
WHERE s.dept_id = d.id;
집계 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- COUNT, SUM, AVG, MAX, MIN
SELECT COUNT(*) FROM Student; -- 전체 행 수
SELECT COUNT(DISTINCT dept) FROM Student; -- 중복 제거 후 수
SELECT AVG(grade) FROM Student WHERE dept = 'CS';
SELECT MAX(grade), MIN(grade) FROM Student;
-- GROUP BY
SELECT dept, COUNT(*) AS cnt, AVG(grade) AS avg_grade
FROM Student
GROUP BY dept;
-- HAVING (그룹에 대한 조건)
SELECT dept, COUNT(*) AS cnt
FROM Student
GROUP BY dept
HAVING COUNT(*) >= 5;
-- WHERE vs HAVING
-- WHERE: 그룹화 전에 행 필터링
-- HAVING: 그룹화 후에 그룹 필터링
INSERT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 단일 행 삽입
INSERT INTO Student (id, name, dept, grade)
VALUES (1001, '홍길동', 'CS', 3);
-- 전체 컬럼 삽입 (컬럼 순서 일치 필요)
INSERT INTO Student VALUES (1002, '김영희', 'EE', 2, 'kim@mail.com', 1);
-- 다중 행 삽입
INSERT INTO Student (id, name, dept, grade) VALUES
(1003, '이철수', 'ME', 4),
(1004, '박민수', 'CS', 1);
-- 서브쿼리로 삽입
INSERT INTO CSStudent (id, name)
SELECT id, name FROM Student WHERE dept = 'CS';
UPDATE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 조건부 수정
UPDATE Student SET grade = 4 WHERE id = 1001;
-- 여러 컬럼 수정
UPDATE Student
SET dept = 'EE', grade = grade + 1
WHERE name = '홍길동';
-- 서브쿼리 활용
UPDATE Student
SET dept_id = (SELECT id FROM Department WHERE name = 'CS')
WHERE id = 1001;
-- 주의: WHERE 없으면 전체 행 수정!
UPDATE Student SET grade = 1; -- 위험!
DELETE
1
2
3
4
5
6
7
8
-- 조건부 삭제
DELETE FROM Student WHERE id = 1001;
-- 전체 삭제 (롤백 가능)
DELETE FROM Student;
-- 주의: WHERE 없으면 전체 행 삭제!
-- TRUNCATE와 달리 DML이므로 롤백 가능
JOIN
조인 종류
1
2
3
4
5
6
1. INNER JOIN: 양쪽 모두 매칭되는 행만
2. LEFT (OUTER) JOIN: 왼쪽 전체 + 오른쪽 매칭
3. RIGHT (OUTER) JOIN: 오른쪽 전체 + 왼쪽 매칭
4. FULL (OUTER) JOIN: 양쪽 전체 (매칭 안 되면 NULL)
5. CROSS JOIN: 카티션 곱 (모든 조합)
6. SELF JOIN: 같은 테이블 간 조인
조인 문법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-- INNER JOIN
SELECT s.name, d.name AS dept_name
FROM Student s
INNER JOIN Department d ON s.dept_id = d.id;
-- LEFT JOIN (왼쪽 테이블의 모든 행 포함)
SELECT s.name, d.name AS dept_name
FROM Student s
LEFT JOIN Department d ON s.dept_id = d.id;
-- dept_id가 NULL인 학생도 결과에 포함 (dept_name은 NULL)
-- RIGHT JOIN
SELECT s.name, d.name AS dept_name
FROM Student s
RIGHT JOIN Department d ON s.dept_id = d.id;
-- 학생이 없는 학과도 결과에 포함
-- FULL OUTER JOIN (MySQL은 미지원, UNION으로 대체)
SELECT s.name, d.name
FROM Student s
LEFT JOIN Department d ON s.dept_id = d.id
UNION
SELECT s.name, d.name
FROM Student s
RIGHT JOIN Department d ON s.dept_id = d.id;
-- CROSS JOIN (카티션 곱)
SELECT s.name, c.course_name
FROM Student s
CROSS JOIN Course c;
-- SELF JOIN (같은 테이블)
SELECT e1.name AS 직원, e2.name AS 상사
FROM Employee e1
JOIN Employee e2 ON e1.manager_id = e2.id;
조인 시각화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
INNER JOIN:
A [ A∩B ] B
^^^^^^
LEFT JOIN:
[A A∩B ] B
^^^^^^^^^^^^
RIGHT JOIN:
A [ A∩B B]
^^^^^^^^^^^^
FULL OUTER JOIN:
[A A∩B B]
^^^^^^^^^^^^^^^^
서브쿼리 (Subquery)
위치별 분류
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 1. WHERE 절 서브쿼리 (가장 흔함)
SELECT name FROM Student
WHERE dept_id = (SELECT id FROM Department WHERE name = 'CS');
-- 2. FROM 절 서브쿼리 (인라인 뷰)
SELECT avg_grade
FROM (
SELECT dept, AVG(grade) AS avg_grade
FROM Student
GROUP BY dept
) AS dept_avg
WHERE avg_grade >= 3;
-- 3. SELECT 절 서브쿼리 (스칼라 서브쿼리)
SELECT name,
(SELECT name FROM Department WHERE id = s.dept_id) AS dept_name
FROM Student s;
서브쿼리 연산자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- IN: 서브쿼리 결과에 포함되는지
SELECT name FROM Student
WHERE dept_id IN (SELECT id FROM Department WHERE building = '공학관');
-- EXISTS: 서브쿼리 결과가 존재하는지 (TRUE/FALSE)
SELECT name FROM Student s
WHERE EXISTS (
SELECT 1 FROM Enrollment e WHERE e.student_id = s.id
);
-- ANY/SOME: 서브쿼리 결과 중 하나라도 만족
SELECT name FROM Student
WHERE grade > ANY (SELECT grade FROM Student WHERE dept = 'CS');
-- ALL: 서브쿼리 결과 모두 만족
SELECT name FROM Student
WHERE grade >= ALL (SELECT grade FROM Student WHERE dept = 'CS');
-- 상관 서브쿼리: 외부 쿼리의 값을 참조
SELECT s.name
FROM Student s
WHERE s.grade > (
SELECT AVG(grade) FROM Student WHERE dept = s.dept
);
IN vs EXISTS
1
2
3
4
5
6
-- IN: 서브쿼리 결과를 먼저 구하고 비교
-- → 서브쿼리 결과가 작을 때 유리
-- EXISTS: 외부 행마다 서브쿼리 실행하여 존재 여부만 확인
-- → 서브쿼리 결과가 클 때 유리 (조기 종료 가능)
-- → 상관 서브쿼리에서 효율적
View
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 뷰 생성 (가상 테이블)
CREATE VIEW CS_Student AS
SELECT id, name, grade
FROM Student
WHERE dept = 'CS';
-- 뷰 조회 (일반 테이블처럼 사용)
SELECT * FROM CS_Student WHERE grade >= 3;
-- 뷰 삭제
DROP VIEW CS_Student;
-- 뷰의 장점
-- 1. 보안: 민감한 컬럼 숨기기
-- 2. 편의성: 복잡한 쿼리를 간단하게
-- 3. 논리적 독립성: 테이블 구조 변경 시 뷰만 수정
-- 뷰의 제한
-- 집계 함수, GROUP BY, DISTINCT를 포함한 뷰는 UPDATE 불가
-- JOIN을 포함한 뷰는 INSERT/DELETE가 제한적
기타 주요 문법
집합 연산
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- UNION: 합집합 (중복 제거)
SELECT name FROM Student WHERE dept = 'CS'
UNION
SELECT name FROM Student WHERE grade = 4;
-- UNION ALL: 합집합 (중복 허용)
SELECT name FROM Student WHERE dept = 'CS'
UNION ALL
SELECT name FROM Student WHERE grade = 4;
-- INTERSECT: 교집합
SELECT name FROM Student WHERE dept = 'CS'
INTERSECT
SELECT name FROM Student WHERE grade = 4;
-- EXCEPT (MINUS): 차집합
SELECT name FROM Student WHERE dept = 'CS'
EXCEPT
SELECT name FROM Student WHERE grade = 4;
CASE 문
1
2
3
4
5
6
7
8
SELECT name, grade,
CASE
WHEN grade = 4 THEN '4학년'
WHEN grade = 3 THEN '3학년'
WHEN grade = 2 THEN '2학년'
ELSE '1학년'
END AS grade_text
FROM Student;
윈도우 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- ROW_NUMBER: 순번
SELECT name, grade,
ROW_NUMBER() OVER (ORDER BY grade DESC) AS rn
FROM Student;
-- RANK: 순위 (동순위 시 건너뜀)
SELECT name, grade,
RANK() OVER (ORDER BY grade DESC) AS rnk
FROM Student;
-- DENSE_RANK: 순위 (동순위 시 안 건너뜀)
SELECT name, grade,
DENSE_RANK() OVER (ORDER BY grade DESC) AS drnk
FROM Student;
-- PARTITION BY: 그룹별 윈도우
SELECT name, dept, grade,
RANK() OVER (PARTITION BY dept ORDER BY grade DESC) AS dept_rank
FROM Student;
-- 집계 윈도우 함수
SELECT name, dept, grade,
AVG(grade) OVER (PARTITION BY dept) AS dept_avg,
SUM(grade) OVER () AS total_sum
FROM Student;
DCL
1
2
3
4
5
6
7
8
9
10
-- 권한 부여
GRANT SELECT, INSERT ON Student TO user1;
GRANT ALL PRIVILEGES ON Student TO admin WITH GRANT OPTION;
-- 권한 회수
REVOKE INSERT ON Student FROM user1;
REVOKE ALL PRIVILEGES ON Student FROM admin CASCADE;
-- WITH GRANT OPTION: 받은 권한을 다른 사용자에게 부여 가능
-- CASCADE: 연쇄적으로 권한 회수
핵심 정리
1
2
3
4
5
6
7
8
1. DDL: CREATE, ALTER, DROP (스키마 정의)
2. DML: SELECT, INSERT, UPDATE, DELETE (데이터 조작)
3. SELECT 실행 순서: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
4. JOIN: INNER, LEFT, RIGHT, FULL, CROSS, SELF
5. 서브쿼리: WHERE(조건), FROM(인라인 뷰), SELECT(스칼라)
6. View: 가상 테이블 (보안, 편의, 독립성)
7. WHERE vs HAVING: 행 필터 vs 그룹 필터
8. TRUNCATE vs DELETE: DDL(롤백불가/빠름) vs DML(롤백가능/느림)
면접 포인트
자주 나오는 질문
Q1. INNER JOIN과 OUTER JOIN의 차이는?
- INNER JOIN: 양쪽 모두 매칭되는 행만 반환
- OUTER JOIN: 매칭되지 않는 행도 포함 (NULL로 채움)
- LEFT/RIGHT/FULL로 어느 쪽을 포함할지 결정
Q2. WHERE와 HAVING의 차이는?
- WHERE: GROUP BY 이전에 개별 행 필터링
- HAVING: GROUP BY 이후에 그룹 필터링
- HAVING은 집계 함수와 함께 사용
Q3. DELETE, TRUNCATE, DROP의 차이는?
- DELETE: DML, 행 단위 삭제, WHERE 가능, 롤백 가능
- TRUNCATE: DDL, 전체 데이터 삭제, 롤백 불가, 빠름
- DROP: DDL, 테이블 자체 삭제 (스키마 + 데이터)
Q4. 서브쿼리 vs 조인, 어떤 것이 더 좋은가?
- 성능은 옵티마이저에 따라 다름 (동일한 경우 많음)
- JOIN: 여러 테이블의 컬럼을 함께 조회할 때 직관적
- 서브쿼리: 한쪽 테이블의 결과를 조건으로 쓸 때 직관적
- EXISTS + 상관 서브쿼리: 존재 여부만 확인할 때 효율적
Q5. View의 장점과 제한은?
- 장점: 보안(컬럼 숨김), 편의성(복잡한 쿼리 캡슐화), 논리적 독립성
- 제한: 집계/GROUP BY 포함 뷰는 갱신 불가
- Materialized View: 결과를 물리적으로 저장 (읽기 성능 향상)
Q6. SELECT 문의 실행 순서는?
- FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
- 이 순서 때문에 SELECT에서 정의한 별칭을 WHERE에서 사용 불가
- HAVING에서는 GROUP BY의 컬럼과 집계 함수만 사용 가능
This post is licensed under CC BY 4.0 by the author.