Ch1. SQLD 자격증 — SQL 기초와 SELECT 완전 정복
SQLD 자격증이란?
**SQLD(SQL Developer, SQL 개발자)**는 한국데이터산업진흥원(K-DATA)이 주관하는 국가공인 자격증입니다. 데이터베이스와 SQL에 대한 기본 지식과 활용 능력을 검증하며, IT 직종 취업·승진에서 가장 많이 요구되는 자격증 중 하나입니다.
주관 기관: 한국데이터산업진흥원(K-DATA)
응시 자격: 제한 없음 (누구나 응시 가능)
공식 사이트: www.dataq.or.kr
시험 구조
| 구분 | 내용 |
|---|---|
| 시험 과목 | 1과목: 데이터 모델링의 이해 / 2과목: SQL 기본 및 활용 |
| 문항 수 | 총 50문항 (객관식 40문항 + 단답형 10문항) |
| 시험 시간 | 80분 |
| 합격 기준 | 총점 60점 이상, 과목별 40% 이상 |
| 시험 형식 | 필기 시험 (PBT 방식) |
합격률: 평균 약 40~50% 수준. 단순 암기보다 SQL 실습과 개념 이해를 병행해야 합격 가능합니다.
데이터 모델링 기초
데이터 모델링이란?
데이터 모델링이란 현실 세계의 정보를 데이터베이스에 저장할 수 있도록 추상화·구조화하는 작업입니다. SQLD 1과목의 핵심이며, 개념적→논리적→물리적 모델링의 3단계로 진행됩니다.
| 단계 | 설명 | 표현 방법 |
|---|---|---|
| 개념적 모델링 | 업무 관점에서 핵심 개체와 관계 파악 | ERD (개체-관계 다이어그램) |
| 논리적 모델링 | 개념을 구체적인 속성과 관계로 표현 | 정규화된 테이블 구조 |
| 물리적 모델링 | 실제 DBMS에 맞게 구현 | DDL, 인덱스, 파티션 등 |
개체-관계 모델 (ERD)
ERD(Entity-Relationship Diagram)는 개체(Entity), 속성(Attribute), 관계(Relationship)로 데이터 구조를 표현합니다.
- 개체(Entity): 독립적으로 존재하는 정보 단위 (예: 고객, 주문, 상품)
- 속성(Attribute): 개체의 특성 (예: 고객ID, 고객명, 전화번호)
- 관계(Relationship): 개체 간의 연관 (예: 고객이 주문을 한다)
카디널리티(Cardinality):
1:1 — 하나의 개체가 하나의 다른 개체와 대응 (예: 직원:사원증)
1:N — 하나의 개체가 여러 개체와 대응 (예: 고객:주문)
M:N — 여러 개체가 여러 개체와 대응 (예: 학생:강의)
M:N 관계는 교차 테이블(Junction Table)로 분리해야 함
정규화 (Normalization)
정규화는 데이터 중복을 제거하고 데이터 무결성을 보장하기 위해 테이블 구조를 단계적으로 개선하는 과정입니다.
제1정규형 (1NF): 모든 속성이 원자값(더 이상 분해 불가)을 가져야 함
- 위반 예: 전화번호 컬럼에 여러 번호를 콤마로 구분해 저장
- 해결: 전화번호를 별도 테이블로 분리
제2정규형 (2NF): 1NF + 부분 함수적 종속 제거
- 복합 기본키를 가진 테이블에서 기본키의 일부에만 종속된 속성을 분리
- 위반 예: (학생ID, 강의ID) → 강의명이 강의ID에만 종속
제3정규형 (3NF): 2NF + 이행 함수적 종속 제거
- A→B, B→C일 때 A→C 같은 이행 종속 제거
- 위반 예: 직원ID → 부서ID → 부서명 (부서명을 별도 테이블로 분리)
반정규화(De-normalization): 성능 향상을 위해 의도적으로 정규화를 되돌리는 것. 조회 성능 개선이 목적이지만 데이터 중복과 갱신 이상 위험이 있음.
SQL 기초
SQL이란?
**SQL(Structured Query Language)**은 관계형 데이터베이스를 관리하고 조작하기 위한 표준 언어입니다. SQL은 목적에 따라 다음과 같이 분류됩니다.
| 분류 | 전체 명칭 | 주요 명령어 | 설명 |
|---|---|---|---|
| DDL | Data Definition Language | CREATE, ALTER, DROP, TRUNCATE | 테이블·스키마 구조 정의 |
| DML | Data Manipulation Language | SELECT, INSERT, UPDATE, DELETE | 데이터 조작 |
| DCL | Data Control Language | GRANT, REVOKE | 권한 부여·취소 |
| TCL | Transaction Control Language | COMMIT, ROLLBACK, SAVEPOINT | 트랜잭션 제어 |
SELECT 문 완전 정복
SELECT 문은 SQL의 핵심입니다. SQLD 시험의 2과목 대부분이 SELECT 관련 문제입니다.
SELECT 기본 구조와 실행 순서
SELECT 컬럼명1, 컬럼명2, ... -- 5. 조회할 열 선택
FROM 테이블명 -- 1. 어느 테이블에서
WHERE 조건식 -- 2. 어떤 조건으로 필터
GROUP BY 그룹화 컬럼 -- 3. 그룹화
HAVING 그룹 조건 -- 4. 그룹 조건 필터
ORDER BY 정렬 기준 -- 6. 정렬
중요: SQL은 작성 순서와 실행 순서가 다릅니다. 실행 순서: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
WHERE 절 주요 조건
-- 비교 연산자
WHERE salary > 3000000
-- BETWEEN: 범위 조건 (경계값 포함)
WHERE hire_date BETWEEN '2020-01-01' AND '2022-12-31'
-- IN: 목록 중 하나
WHERE dept_id IN (10, 20, 30)
-- LIKE: 패턴 매칭
WHERE name LIKE '김%' -- '김'으로 시작
WHERE name LIKE '%수%' -- '수'가 포함
WHERE name LIKE '_민' -- 두 글자이고 '민'으로 끝남
-- IS NULL / IS NOT NULL
WHERE bonus IS NULL
WHERE manager_id IS NOT NULL
GROUP BY와 HAVING
-- 부서별 평균 급여 조회 (평균 급여 300만원 이상인 부서만)
SELECT dept_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept_id
HAVING AVG(salary) >= 3000000
ORDER BY avg_salary DESC;
WHERE vs HAVING: WHERE는 개별 행을 필터 (GROUP BY 이전), HAVING은 그룹화된 결과를 필터 (GROUP BY 이후)
JOIN 완전 정복
JOIN은 두 개 이상의 테이블을 연결하여 데이터를 조회하는 연산입니다.
INNER JOIN (내부 조인)
두 테이블에서 조인 조건을 만족하는 행만 반환합니다.
-- 주문이 있는 고객만 조회
SELECT c.customer_name, o.order_date, o.amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;
LEFT OUTER JOIN (왼쪽 외부 조인)
왼쪽 테이블의 모든 행을 반환하고, 오른쪽 테이블에 매칭이 없으면 NULL을 표시합니다.
-- 주문이 없는 고객도 포함하여 조회
SELECT c.customer_name, o.order_date
FROM customers c
LEFT OUTER JOIN orders o ON c.customer_id = o.customer_id;
-- 주문이 없는 고객은 order_date가 NULL로 표시됨
CROSS JOIN (교차 조인)
두 테이블의 모든 행의 조합을 반환합니다. m행 × n행 = m×n행.
SELECT * FROM colors CROSS JOIN sizes;
-- 색상 3가지 × 사이즈 4가지 = 12행 반환
JOIN 정리
| JOIN 종류 | 반환 행 |
|---|---|
| INNER JOIN | 두 테이블 모두 조건 일치하는 행 |
| LEFT OUTER JOIN | 왼쪽 테이블 전체 + 오른쪽 일치 행(없으면 NULL) |
| RIGHT OUTER JOIN | 오른쪽 테이블 전체 + 왼쪽 일치 행(없으면 NULL) |
| FULL OUTER JOIN | 양쪽 테이블 전체 (불일치는 NULL) |
| CROSS JOIN | 모든 행의 카테시안 곱 |
서브쿼리 (Subquery)
서브쿼리는 SQL 문 안에 포함된 또 다른 SELECT 문입니다.
단일행 서브쿼리
서브쿼리가 한 행만 반환. 단일행 비교 연산자(=, >, <, >=, <=, <>) 사용.
-- 평균 급여보다 높은 직원 조회
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
다중행 서브쿼리
서브쿼리가 여러 행을 반환. IN, ANY, ALL 연산자 사용.
-- 영업부 직원과 같은 급여를 받는 다른 직원 조회
SELECT name, salary
FROM employees
WHERE salary IN (SELECT salary FROM employees WHERE dept = '영업부')
AND dept <> '영업부';
다중열 서브쿼리
서브쿼리가 여러 열을 반환.
-- 각 부서에서 가장 높은 급여를 받는 직원 조회
SELECT name, dept_id, salary
FROM employees
WHERE (dept_id, salary) IN (
SELECT dept_id, MAX(salary)
FROM employees
GROUP BY dept_id
);
스칼라 서브쿼리 & 인라인 뷰
-- 스칼라 서브쿼리: SELECT 절에서 단일값 반환
SELECT name,
salary,
(SELECT AVG(salary) FROM employees) AS avg_salary
FROM employees;
-- 인라인 뷰: FROM 절에서 가상 테이블처럼 사용
SELECT dept_id, avg_salary
FROM (SELECT dept_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept_id) AS dept_avg
WHERE avg_salary > 3500000;
집계함수 (Aggregate Functions)
집계함수는 여러 행의 값을 계산하여 하나의 결과를 반환합니다.
| 함수 | 설명 | NULL 처리 |
|---|---|---|
| COUNT(*) | 전체 행 수 | NULL 포함 |
| COUNT(컬럼) | NULL이 아닌 값의 수 | NULL 제외 |
| SUM(컬럼) | 합계 | NULL 무시 |
| AVG(컬럼) | 평균 | NULL 무시 (분모에서도 제외) |
| MAX(컬럼) | 최댓값 | NULL 무시 |
| MIN(컬럼) | 최솟값 | NULL 무시 |
SELECT
COUNT(*) AS total_rows,
COUNT(bonus) AS has_bonus, -- bonus가 NULL이 아닌 행 수
SUM(salary) AS total_salary,
AVG(salary) AS avg_salary,
MAX(salary) AS max_salary,
MIN(salary) AS min_salary
FROM employees;
윈도우 함수 (Window Functions)
윈도우 함수는 파티션 내 행들 간의 계산을 수행합니다. GROUP BY와 달리 행을 집계하지 않고 각 행에 결과를 추가합니다.
기본 문법
함수명() OVER (
PARTITION BY 그룹컬럼 -- 윈도우(파티션) 정의
ORDER BY 정렬컬럼 -- 정렬 기준
)
순위 함수
SELECT
name,
dept_id,
salary,
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rank_val,
DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dense_rank_val,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS row_num
FROM employees;
| 함수 | 동점 처리 | 예시 (동점 2명) |
|---|---|---|
| RANK() | 같은 순위, 다음 순위 건너뜀 | 1, 2, 2, 4 |
| DENSE_RANK() | 같은 순위, 연속 | 1, 2, 2, 3 |
| ROW_NUMBER() | 동점 없이 고유 번호 | 1, 2, 3, 4 |
LAG / LEAD 함수
이전 행과 다음 행의 값을 현재 행에서 참조합니다.
SELECT
order_date,
amount,
LAG(amount, 1) OVER (ORDER BY order_date) AS prev_amount, -- 이전 행
LEAD(amount, 1) OVER (ORDER BY order_date) AS next_amount -- 다음 행
FROM orders;
NULL 처리 함수
NULL은 “알 수 없음”을 의미하며, 일반 비교 연산자로는 비교할 수 없습니다.
-- NULL 관련 주의사항
NULL = NULL -- 결과: NULL (TRUE가 아님!)
NULL <> NULL -- 결과: NULL
-- NULL 비교는 반드시 IS NULL / IS NOT NULL 사용
-- NVL (Oracle): NULL이면 대체값 반환
SELECT NVL(bonus, 0) FROM employees; -- bonus가 NULL이면 0
-- COALESCE (표준 SQL): 첫 번째 NULL이 아닌 값 반환
SELECT COALESCE(bonus, commission, 0) FROM employees;
-- NULLIF: 두 값이 같으면 NULL, 다르면 첫 번째 값 반환
SELECT NULLIF(score, 0) FROM students; -- score가 0이면 NULL (0으로 나누기 방지에 활용)
-- CASE 문으로 NULL 처리
SELECT name,
CASE WHEN bonus IS NULL THEN '보너스 없음'
ELSE TO_CHAR(bonus) END AS bonus_info
FROM employees;
학습 체크리스트
- SQLD 시험 구조(과목·문항·합격기준)를 설명할 수 있다
- 데이터 모델링 3단계와 ERD 구성 요소를 설명할 수 있다
- 제1~3정규형의 위반 사례와 해결 방법을 알고 있다
- SELECT 문의 실행 순서(FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY)를 외웠다
- INNER JOIN, LEFT OUTER JOIN, CROSS JOIN의 차이를 설명할 수 있다
- 단일행·다중행·다중열 서브쿼리를 구분할 수 있다
- COUNT(*) vs COUNT(컬럼)의 NULL 처리 차이를 안다
- RANK, DENSE_RANK, ROW_NUMBER의 동점 처리 방식 차이를 안다
- NVL, COALESCE, NULLIF의 차이를 설명할 수 있다
핵심 개념 카드
SELECT 실행 순서 ★★★★★ : FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. 작성 순서와 실행 순서가 다르다는 점이 시험 단골 출제 포인트. 암기 포인트: “프웨그해셀오” (FROM-WHERE-GROUP-HAVING-SELECT-ORDER)
WHERE vs HAVING ★★★★★ : WHERE는 개별 행 필터(GROUP BY 이전), HAVING은 그룹화된 결과 필터(GROUP BY 이후). 집계함수는 WHERE에서 사용 불가, HAVING에서만 사용 가능. 암기 포인트: WHERE=행 필터, HAVING=그룹 필터
RANK vs DENSE_RANK vs ROW_NUMBER ★★★★★ : RANK: 동점 시 같은 순위 부여, 다음 순위 건너뜀 (1,2,2,4). DENSE_RANK: 동점 허용, 연속 순위 (1,2,2,3). ROW_NUMBER: 동점 없이 고유 번호 (1,2,3,4). 암기 포인트: DENSE(밀집)는 빈 번호 없이 연속
INNER JOIN vs LEFT OUTER JOIN ★★★★★ : INNER JOIN은 양쪽 테이블에 모두 일치하는 행만 반환. LEFT JOIN은 왼쪽 테이블 전체 + 오른쪽 미일치 시 NULL. “고객이 주문을 했든 안 했든 모든 고객 보기”는 LEFT JOIN.
NULL 처리 연산자 ★★★★ : NULL과 어떤 값의 비교도 NULL. 반드시 IS NULL / IS NOT NULL 사용. NVL(Oracle)과 COALESCE(표준)는 NULL 대체값 반환. NULLIF는 두 값이 같을 때 NULL 반환. 암기 포인트: NULL = NULL은 FALSE가 아니라 NULL
정규화 1NF~3NF ★★★★ : 1NF=원자값, 2NF=부분함수종속 제거(복합키 테이블), 3NF=이행함수종속 제거. 반정규화는 성능을 위해 의도적으로 되돌리는 것.
집계함수와 NULL ★★★★ : COUNT(*)는 NULL 포함 전체 행 수. COUNT(컬럼)는 NULL을 제외한 행 수. SUM/AVG/MAX/MIN은 모두 NULL을 무시하고 계산.
실전 퀴즈
Q1. 다음 SQL의 실행 결과에서 순위 함수 RANK, DENSE_RANK, ROW_NUMBER의 차이는?
점수가 90, 90, 80인 3명의 경우: RANK()는 1,1,3 / DENSE_RANK()는 1,1,2 / ROW_NUMBER()는 1,2,3을 반환합니다. RANK는 동점자 수만큼 다음 순위를 건너뛰고, DENSE_RANK는 건너뛰지 않으며, ROW_NUMBER는 동점을 허용하지 않습니다.
Q2. WHERE salary > AVG(salary)라는 쿼리가 오류가 나는 이유는?
WHERE 절에서는 집계함수를 사용할 수 없습니다. 집계함수는 GROUP BY 이후에 적용되기 때문에 WHERE 처리 시점에는 값이 결정되지 않습니다. 올바른 방법은 서브쿼리를 사용하는 것입니다:
WHERE salary > (SELECT AVG(salary) FROM employees).
Q3. COUNT(*)와 COUNT(bonus)의 차이는?
COUNT()는 NULL을 포함한 전체 행 수를 반환합니다. COUNT(bonus)는 bonus 컬럼이 NULL이 아닌 행의 수만 반환합니다. 따라서 bonus가 NULL인 직원이 있으면 COUNT() > COUNT(bonus)가 됩니다.
OIYO 편집부
Content Editor지식 인큐베이터이자 전문 콘텐츠 크리에이터. 경영, 경제, 법률 및 실생활에 유용한 실무/자격증 중심의 깊이 있는 정보를 연구하고 공유합니다.