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 편집부
편집부OIYO 편집부는 경제·법률·생활·자기이해 주제를 1차 자료와 공개 통계로 검증해 정리합니다. 모든 글은 출처 표기와 정기 점검을 거쳐 실용성과 정확성을 함께 유지합니다.