Ch2. SQLD 자격증 — DML 심화와 JOIN 완전 정복
DML(Data Manipulation Language) 심화
SQLD 2과목의 핵심인 DML은 INSERT, UPDATE, DELETE, SELECT를 모두 포함합니다. 시험에서는 특히 문법의 미묘한 차이와 실행 결과 예측을 묻습니다.
INSERT 심화
기본 INSERT
-- 단일 행 삽입
INSERT INTO employees (emp_id, name, dept_id, salary)
VALUES (101, '홍길동', 10, 5000000);
-- 전체 컬럼 순서대로 삽입 (컬럼명 생략)
INSERT INTO employees
VALUES (102, '이순신', 20, 6000000, SYSDATE);
서브쿼리를 이용한 다중 행 삽입
-- 다른 테이블에서 데이터를 가져와 삽입
INSERT INTO emp_backup
SELECT emp_id, name, salary
FROM employees
WHERE dept_id = 10;
핵심: INSERT ~ SELECT는 VALUES를 쓰지 않습니다. 컬럼 수와 타입이 일치해야 합니다.
UPDATE 심화
-- 기본 UPDATE
UPDATE employees
SET salary = salary * 1.1
WHERE dept_id = 10;
-- 서브쿼리를 이용한 UPDATE
UPDATE employees
SET salary = (SELECT AVG(salary) FROM employees)
WHERE dept_id = 20;
UPDATE 시 주의사항
-- WHERE 조건이 없으면 전체 행이 변경됨!
UPDATE employees SET salary = 0; -- 위험!
-- 서브쿼리가 여러 행 반환 시 오류
UPDATE employees
SET dept_id = (SELECT dept_id FROM departments); -- 오류 (다중 행 반환)
DELETE vs TRUNCATE vs DROP
| 구분 | DELETE | TRUNCATE | DROP |
|---|---|---|---|
| 종류 | DML | DDL | DDL |
| WHERE 조건 | 가능 | 불가 | 불가 |
| 롤백 | 가능 | 불가 (일부 DB 가능) | 불가 |
| 로그 기록 | 행별 기록 | 최소 로그 | 최소 로그 |
| 속도 | 느림 | 빠름 | 빠름 |
| 테이블 구조 | 유지 | 유지 | 삭제 |
| AUTO INCREMENT | 초기화 안 됨 | 초기화 됨 | — |
DELETE FROM employees WHERE dept_id = 10; -- 조건부 삭제
TRUNCATE TABLE employees; -- 전체 삭제 (빠름)
DROP TABLE employees; -- 테이블 자체 삭제
JOIN 완전 정복
JOIN은 SQLD에서 가장 많이 출제되는 개념입니다. 각 JOIN 유형의 결과 집합을 정확히 이해해야 합니다.
INNER JOIN (내부 조인)
두 테이블에서 조인 조건을 만족하는 행만 반환합니다.
-- ANSI 표준 문법
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
-- Oracle 전통 문법
SELECT e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.dept_id;
예시 결과:
employees: [A:10, B:20, C:30]
departments: [10:'영업', 20:'개발', 40:'인사']
INNER JOIN → A:영업, B:개발 (30, 40은 제외)
LEFT OUTER JOIN (좌측 외부 조인)
왼쪽 테이블의 모든 행을 반환하고, 오른쪽 테이블에 없으면 NULL.
SELECT e.name, d.dept_name
FROM employees e
LEFT OUTER JOIN departments d ON e.dept_id = d.dept_id;
-- 결과: A:영업, B:개발, C:NULL
RIGHT OUTER JOIN (우측 외부 조인)
오른쪽 테이블의 모든 행을 반환하고, 왼쪽에 없으면 NULL.
SELECT e.name, d.dept_name
FROM employees e
RIGHT OUTER JOIN departments d ON e.dept_id = d.dept_id;
-- 결과: A:영업, B:개발, NULL:인사
FULL OUTER JOIN
양쪽 테이블의 모든 행을 반환, 매칭되지 않으면 NULL.
SELECT e.name, d.dept_name
FROM employees e
FULL OUTER JOIN departments d ON e.dept_id = d.dept_id;
-- 결과: A:영업, B:개발, C:NULL, NULL:인사
CROSS JOIN (교차 조인)
두 테이블의 카티션 곱(모든 조합) — 조인 조건 없음.
SELECT e.name, d.dept_name
FROM employees e
CROSS JOIN departments d;
-- employees 3행 × departments 3행 = 9행
SELF JOIN (자기 참조 조인)
같은 테이블을 두 번 참조합니다.
-- 직원의 매니저 이름 조회
SELECT e.name AS 직원, m.name AS 매니저
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.emp_id;
서브쿼리(Subquery) 완전 정복
단일 행 서브쿼리
서브쿼리가 1개의 값을 반환 → =, >, <, >=, <=, <> 연산자 사용
-- 평균 급여보다 높은 직원 조회
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
다중 행 서브쿼리
서브쿼리가 여러 행을 반환 → IN, ANY, ALL 연산자 사용
-- IN: 특정 부서 직원 중 한 명이라도 일치
SELECT name FROM employees
WHERE dept_id IN (SELECT dept_id FROM departments WHERE location = '서울');
-- ANY: 최소 하나라도 만족
SELECT name FROM employees
WHERE salary > ANY (SELECT salary FROM managers);
-- ALL: 모두 만족
SELECT name FROM employees
WHERE salary > ALL (SELECT salary FROM interns);
상관(Correlated) 서브쿼리
외부 쿼리의 값을 서브쿼리 안에서 참조 — 행마다 서브쿼리 실행
-- 각 직원의 급여가 해당 부서 평균보다 높은 경우
SELECT e.name, e.salary
FROM employees e
WHERE e.salary > (
SELECT AVG(salary)
FROM employees
WHERE dept_id = e.dept_id -- 외부 쿼리의 dept_id 참조
);
GROUP BY 고급 활용
HAVING vs WHERE
-- WHERE: 그룹화 전 필터
-- HAVING: 그룹화 후 필터 (집계함수 사용 가능)
SELECT dept_id, AVG(salary) AS avg_sal
FROM employees
WHERE hire_date >= '2020-01-01' -- 행 필터
GROUP BY dept_id
HAVING AVG(salary) > 5000000; -- 그룹 필터
ROLLUP과 CUBE
-- ROLLUP: 계층적 소계
SELECT dept_id, job_title, SUM(salary)
FROM employees
GROUP BY ROLLUP(dept_id, job_title);
-- dept_id별 소계 + 전체 합계 자동 생성
-- CUBE: 모든 조합의 소계
GROUP BY CUBE(dept_id, job_title);
-- dept_id별, job_title별, 전체 소계 모두 생성
윈도우 함수(Window Function) 기초
SQLD에서 점점 비중이 높아지는 파트입니다.
-- ROW_NUMBER: 고유 순번
SELECT name, salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS rank
FROM employees;
-- RANK: 동점자에게 같은 순위, 다음 순위 건너뜀
-- DENSE_RANK: 동점자에게 같은 순위, 다음 순위 건너뛰지 않음
-- 부서별 급여 순위
SELECT name, dept_id, salary,
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dept_rank
FROM employees;
-- LAG/LEAD: 이전/다음 행 참조
SELECT name, salary,
LAG(salary, 1) OVER (ORDER BY hire_date) AS prev_salary
FROM employees;
실전 퀴즈 5문항
Q1. 다음 SQL의 결과 행 수는?
SELECT * FROM A CROSS JOIN B;
-- A: 4행, B: 3행
- ① 3 ② 4 ③ 7 ④ 12
정답: ④ (4 × 3 = 12)
Q2. TRUNCATE와 DELETE의 차이로 옳은 것은?
- ① TRUNCATE는 WHERE 조건 사용 가능
- ② DELETE는 롤백 불가능
- ③ TRUNCATE는 DDL이고 일반적으로 롤백 불가
- ④ 두 명령어의 실행 속도는 동일
정답: ③
Q3. LEFT OUTER JOIN에서 오른쪽 테이블에 매칭되는 행이 없을 때 반환값은?
- ① 0 ② 빈 문자열 ③ NULL ④ 오류
정답: ③
Q4. 상관 서브쿼리와 일반 서브쿼리의 가장 큰 차이는?
- ① 반환 행 수
- ② 외부 쿼리의 값을 내부에서 참조하는지 여부
- ③ WHERE 절 사용 여부
- ④ 집계함수 사용 여부
정답: ②
Q5. HAVING 절에 대한 설명으로 옳은 것은?
- ① GROUP BY 없이 단독 사용 가능
- ② 집계함수 결과를 조건으로 사용할 수 있다
- ③ WHERE 절보다 먼저 실행된다
- ④ 개별 행을 필터링한다
정답: ②
OIYO 편집부
Content Editor지식 인큐베이터이자 전문 콘텐츠 크리에이터. 경영, 경제, 법률 및 실생활에 유용한 실무/자격증 중심의 깊이 있는 정보를 연구하고 공유합니다.