
안녕하세요, 공부하는 감자입니다 🥔
실기 SQL 문제는 문법 객관식이 아니라 "이 쿼리의 결과 테이블을 쓰시오"입니다. 그래서 문법을 외우는 것만으로는 부족하고, 결과를 연필로 직접 그려낼 수 있어야 해요.
저도 처음엔 눈으로 풀다가 JOIN 행 개수와 NULL 처리에서 계속 틀렸습니다. 이 글은 제가 손으로 그려가며 잡은 패턴들이에요.
JOIN 4종 — 행이 몇 개 남는지가 핵심
연습용 테이블 두 개를 두고 볼게요.
MEMBER
| mem_id | name | team_id |
|---|---|---|
| 1 | 한 | 100 |
| 2 | 정 | 200 |
| 3 | 윤 | NULL |
TEAM
| team_id | team_name |
|---|---|
| 100 | 기획 |
| 200 | 디자인 |
| 300 | 총무 |
INNER JOIN은 양쪽 모두 매칭되는 행만 남깁니다.
SELECT m.name, t.team_name
FROM MEMBER m
JOIN TEAM t ON m.team_id = t.team_id;
| name | team_name |
|---|---|
| 한 | 기획 |
| 정 | 디자인 |
team_id가 NULL인 윤씨와 소속 인원이 없는 총무팀은 빠져요.
LEFT OUTER JOIN은 왼쪽(MEMBER)을 전부 살리고 매칭 안 되는 오른쪽을 NULL로 채웁니다 — 위 결과에 윤 | NULL 행이 추가돼요. RIGHT OUTER JOIN은 반대로 오른쪽(TEAM) 기준이라 NULL | 총무가 추가되고, FULL OUTER JOIN은 양쪽 미매칭 행을 모두 포함해서 네 행이 됩니다.
JOIN 결과 행 개수 문제에서 틀리는 지점은 거의 항상 NULL 매칭과 미매칭 행의 포함 여부예요.
표를 그려서 한 행씩 매칭 체크를 하는 게 가장 확실합니다.
WHERE와 HAVING — 적용 시점이 다릅니다
둘의 차이는 두 줄짜리 표로 정리됩니다.
| 구분 | 적용 시점 | 집계 함수 |
|---|---|---|
| WHERE | GROUP BY 전 (행 필터) | 사용 불가 |
| HAVING | GROUP BY 후 (그룹 필터) | 사용 가능 |
-- 연봉 3000 이상인 회원만 대상으로, 팀별 인원이 2명 이상인 팀
SELECT team_id, COUNT(*) AS cnt
FROM MEMBER
WHERE salary >= 3000
GROUP BY team_id
HAVING COUNT(*) >= 2;
헷갈릴 때는 실행 순서를 떠올리면 돼요.
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
이 순서 하나로 SQL 문제의 혼란이 절반은 줄어듭니다.
서브쿼리 세 종류
단일행 서브쿼리는 결과가 한 행이라 =, >, <로 비교합니다.
SELECT * FROM MEMBER
WHERE salary = (SELECT MAX(salary) FROM MEMBER);
다중행이면 IN(목록 중 하나와 일치), ANY(하나라도 만족), ALL(전부 만족)을 씁니다.
상관 서브쿼리는 바깥 쿼리의 컬럼을 안쪽에서 참조하는 구조예요.
-- 자기 팀 평균 연봉 이상인 회원
SELECT m.name, m.salary
FROM MEMBER m
WHERE m.salary >= (SELECT AVG(salary)
FROM MEMBER
WHERE team_id = m.team_id);
바깥의 행마다 서브쿼리가 한 번씩 실행된다는 점이 일반 서브쿼리와의 차이입니다.
윈도우 함수 — RANK 삼형제 구분
집계 함수와 달리 행 수를 줄이지 않고 순위를 계산합니다. 점수가 95, 90, 90, 80인 네 명에게 세 함수를 동시에 걸면:
SELECT name, score,
RANK() OVER (ORDER BY score DESC) AS rk,
DENSE_RANK() OVER (ORDER BY score DESC) AS drk,
ROW_NUMBER() OVER (ORDER BY score DESC) AS rn
FROM MEMBER;
| name | score | rk | drk | rn |
|---|---|---|---|---|
| P | 95 | 1 | 1 | 1 |
| Q | 90 | 2 | 2 | 2 |
| R | 90 | 2 | 2 | 3 |
| S | 80 | 4 | 3 | 4 |
- RANK — 동점에 같은 순위, 다음 순위는 건너뜀 (1, 2, 2, 4)
- DENSE_RANK — 동점에 같은 순위, 건너뛰지 않음 (1, 2, 2, 3)
- ROW_NUMBER — 동점이어도 행마다 고유 번호 (1, 2, 3, 4)
"RANK와 DENSE_RANK의 차이"가 단답으로 나오면 답은 '건너뛰는가'입니다.
LAG(col, n)은 n행 이전 값, LEAD(col, n)은 n행 다음 값이고, PARTITION BY를 붙이면 그룹별로 순위를 따로 매기되 행 수는 유지돼요 — GROUP BY와의 차이가 바로 이 "행 유지"입니다.
집합 연산과 NULL
UNION은 중복 제거 합집합, UNION ALL은 중복 포함이에요(그래서 대용량에선 UNION ALL이 빠릅니다). INTERSECT는 교집합, MINUS는 차집합(Oracle 문법, 표준은 EXCEPT).
NULL은 따로 챙겨야 하는 영역입니다.
SELECT NULL = NULL; -- NULL (true 아님)
SELECT NULL IS NULL; -- true
SELECT 10 + NULL; -- NULL (산술 연산 결과도 NULL)
비교는 반드시 IS NULL / IS NOT NULL로 해야 하고, COUNT(*)는 전체 행 수지만 COUNT(col)은 그 컬럼이 NULL인 행을 빼고 셉니다.
시험장에서의 풀이 순서
- 테이블 스키마와 샘플 데이터를 메모지에 옮겨 적기 — 쿼리만 노려보지 않기
- JOIN·WHERE를 적용해 남는 행에 체크 표시
- GROUP BY가 있으면 그룹으로 묶은 뒤 집계 컬럼 계산
- HAVING으로 그룹 거르고, ORDER BY는 맨 마지막
- NULL 컬럼이 보이면
=비교가 숨어 있지 않은지 한 번 더 확인
저는 JOIN과 윈도우 함수 유형을 문어CBT에서 반복해서 풀었는데, 무료 모의고사인데도 결과 테이블 쓰는 연습이 충분히 되더라고요.
SQLD·정처기·컴활·ADsP 무료 CBT 모의고사
기출 변형 문제, 자동 채점, 회차별 실력 추적까지 가입 없이 바로 풀어보세요.
sqldpass.com
쿼리를 보자마자 결과 형태가 머릿속에 그려질 때까지 같은 패턴을 돌리는 게 SQL 파트의 가장 빠른 길입니다.
'자격증 > 정보처리기사 실기' 카테고리의 다른 글
| 정처기 실기 보안 정리, 헷갈리는 용어 4쌍 (0) | 2026.06.23 |
|---|---|
| 정처기 실기 파이썬 출력 문제 정리 (슬라이싱·기본 인자) (0) | 2026.06.23 |
| 정처기 실기 기출복원 최신 회차 모음 — 2026년 1회까지 무료 풀이 (0) | 2026.06.23 |
| 정처기 실기 접수, 모레 6/25 마감입니다 (2026년 2회, 시험 7/18~8/5) (0) | 2026.06.23 |
| 정보처리기사 응시료·환불 규정 정리 — 접수 취소하면 얼마 돌려받을까 (2026 큐넷) (0) | 2026.06.22 |