E-2: SQLD 모의고사 & 실전
목차 29
안녕하세요, 홍순구 튜터예요. 지난 시간엔 같은 인스타그램 쿼리를 Oracle 사투리와 ANSI 표준어, 두 방언으로 나란히 적어 봤죠. NVL↔COALESCE, (+)↔OUTER JOIN, MINUS↔EXCEPT까지 — SQL 문법은 이렇게 모델링부터 윈도우 함수, 방언 대조까지 한 바퀴를 다 돌았어요.
이제 마지막 관문이에요. 그동안 배운 걸 전부 꺼내서, SQLD 시험장에서 통하는 실전 감각을 만들 차례죠. 오늘은 새 문법을 배우지 않아요. 대신 50문항을 90분 안에 푸는 호흡, 헷갈리게 깔아 둔 오답 보기를 걸러내는 눈, 그리고 과목별 시간 배분과 과락을 막는 전략까지 — 시험장에서 바로 쓰는 것들을 모아요. 문제는 전부 우리가 쌓아 온 인스타그램 데이터 위에서 나와요.
A~D 에서 배운 SQL 전부
모델링 · 함수 · 집계 · 조인 · 서브쿼리 · 계층형 · 윈도우 · 방언
│
▼
[ SQLD 모의고사 ] 50문항 · 90분
│ 결과 예측 · 오답 보기 거르기 · 시간 배분
▼
SQLD 합격 총점 60점 이상 + 과목별 과락 방어
💡 오늘 수업의 핵심 — "배운 SQL을 시험장의 50문항으로"
쿼리를 읽고 결과를 미리 예측해, 헷갈리는 오답 보기를 근거 있게 걸러내고, 90분을 과목별로 배분해 과락 없이 합격선을 넘는다.
🎯 학습 목표
- SQLD 시험 구조(50문항·90분·60점·과목별 40% 과락)를 알고, 90분을 과목별로 배분할 수 있다.
- 쿼리를 실행하지 않고도 결과를 예측해, 출제자가 깔아 둔 오답 보기를 걸러낼 수 있다.
- 1·2과목 전 범위(모델링부터 윈도우·방언까지)의 기출 유형을 인스타 도메인 모의 문항으로 풀어낸다.
Step 1: "시험장에 들어서기 전 — SQLD 90분의 지도"
시험은 실력 게임이기도 하지만, 90분을 어떻게 쓰느냐의 게임이기도 해요. 그래서 제일 먼저, 우리가 들어갈 시험장의 지도부터 펼쳐 볼게요.
SQLD는 객관식 50문항을 90분 동안 풀어요. 100점 만점에 60점 이상이면 합격이죠. 그런데 총점만 보는 게 아니에요. 두 과목으로 나뉘고, 한 과목이라도 40% 미만이면 총점이 아무리 높아도 불합격이에요. 이걸 과락이라고 해요.
SQLD 50문항 · 90분 · 100점 만점 · 60점 이상 합격
1과목 데이터 모델링의 이해 10문항 · 20점 → 과락선 8점 (4문항)
2과목 SQL 기본 및 활용 40문항 · 80점 → 과락선 32점 (16문항)
합격 = 총점 60점 이상 그리고 두 과목 모두 40% 이상
배점이 곧 학습 비중이에요. 2과목 SQL이 80점이라, 우리가 C·D 카테고리에서 함수·집계·조인·서브쿼리·윈도우에 가장 많은 시간을 쏟은 이유가 여기 있어요. 시험장에서도 시간을 2과목에 몰아야 하죠.
90분을 어떻게 쓸까 (문항당 평균 108초)
1과목 모델링 10문항 ──→ 약 10~15분 (이론 위주, 빠르게 확보)
2과목 SQL 40문항 ──→ 약 60~65분 (계산·결과 예측, 집중)
검토 · 마킹 점검 ──→ 약 10분 (자신 없는 문항 재확인)
1과목 모델링은 이론이라 한 문항을 1분 안에 끊을 수 있어요. 여기서 아낀 시간을 2과목 SQL의 결과 예측 문제에 쓰는 거죠. 오늘은 본문 곳곳에 ★빈출(자주 나오는 유형)과 ⚠️(틀리기 쉬운 함정)을 표시해 둘게요. 시험 포인트만 콕 집어 보세요.
⚠️ 함정 — 총점 60점을 넘겨도 한 과목이 과락이면 떨어져요. 1과목(20점)을 "어차피 배점 적으니 버리자"고 통째로 포기하면, 8점(4문항)을 못 채워 과락으로 탈락할 수 있어요. 배점이 작아도 과락선은 지켜야 해요.
💡 한 줄 정리
SQLD는 50문항 90분, 총점 60점 이상에 과목별 40% 과락이 걸려 있다. 2과목(80점)에 시간을 몰되, 1과목 과락선(8점)도 반드시 넘긴다.
🙋 학생 질문 — "튜터님, 총점이 60점만 넘으면 합격 아니에요?"
아니에요, 여기서 많이들 걸려요. 합격엔 조건이 두 개예요. 총점 60점 이상이 첫째고, 둘째가 "두 과목 모두 40% 이상"이에요. 예를 들어 2과목 SQL을 80점 만점에 60점 받고 1과목 모델링을 20점 만점에 5점 받으면, 총점은 65점이라 60점을 넘지만 1과목이 25%(과락선 40% 미만)라 불합격이에요. 그래서 1과목도 최소 8점(4문항)은 확보하는 전략이 필요해요.
Step 2: "오답 보기를 걸러내는 눈 — 쿼리 결과 예측법"
SQLD 객관식의 핵심 기술은 딱 하나예요. 쿼리를 읽고 결과를 머릿속으로 예측하는 것. 실행 버튼이 없는 시험장에서, 보기 다섯 개 중 진짜 답을 고르려면 결과를 미리 그릴 수 있어야 하죠.
출제자는 그냥 답을 주지 않아요. 진짜 답과 한 끗 차이 나는 함정 보기를 꼭 섞어 둬요. 이 오답 보기(distractor)에는 단골 패턴이 네 가지 있어요.
오답 보기 4대 패턴
① 실행 순서 WHERE vs HAVING · ROWNUM vs ORDER BY 순서를 착각하게
② NULL =NULL · NOT IN+NULL · COUNT(컬럼)이 NULL을 빼는 걸 잊게
③ 정렬·방향 ASC/DESC · NULLS FIRST/LAST · 차집합 A-B vs B-A
④ 방언 혼동 결과는 같은데 Oracle 고유/표준 문법을 틀렸다고 오인
이 패턴을 알고 결과를 직접 세어 보면 함정이 보여요. 바로 워밍업 두 문항으로 감을 잡아 볼게요.
[문제 1] ★빈출 회원 테이블은 12명이고, 그중 bio(소개글)가 NULL인 회원이 4명이다. 다음 쿼리의 결과는?
-- sql/queries/E2_mock_exam.sql
SELECT COUNT(*), COUNT(bio) FROM member;
① 12, 12 ② 12, 8 ③ 8, 8 ④ 8, 12 ⑤ 12, 4
✅ 정답 보기
정답: ②
COUNT(*)는 행의 개수라 NULL과 무관하게 12. COUNT(bio)는 bio가 NULL인 4명을 빼고 세니까 8이에요. 컬럼을 넣은 COUNT는 그 컬럼이 NULL인 행을 빼고 센다는 것 — 2과목 단골이에요.
[문제 2] "좋아요 Top-3 게시물"을 뽑으려 한 아래 쿼리의 실제 결과는? (좋아요 실제 1~3위는 post 12·1·40)
SELECT post_id, likes FROM (
SELECT post_id, COUNT(*) AS likes
FROM post_like GROUP BY post_id
) WHERE ROWNUM <= 3
ORDER BY likes DESC;
① post 12, 1, 40 ② post 1, 5, 2 ③ post 40, 18, 13 ④ 오류가 난다 ⑤ post 12, 5, 2
✅ 정답 보기
정답: ②
ROWNUM은 ORDER BY보다 먼저 매겨져요. 그래서 정렬되지 않은 상태의 아무 3건(post 1·5·2)을 먼저 집고, 그 셋만 뒤늦게 정렬해요. 진짜 1등인 post 12(좋아요 6개)는 아예 후보에 들지도 못하고 사라지죠. 보기 ①은 "올바른 결과"라 함정이에요. 올바르게 뽑으려면 인라인뷰 안에서 먼저 정렬한 뒤 바깥에서 ROWNUM으로 잘라야 해요.
⚠️ 함정 — 보기엔 늘 "올바른 결과"가 미끼로 섞여 있어요. 쿼리가 의도대로 동작한다고 믿지 말고, 실행 순서까지 따져 실제 결과를 세어 보세요.
💡 한 줄 정리
객관식은 결과 예측 게임이다. 오답 보기 4대 패턴(실행순서·NULL·정렬·방언)을 알고 결과를 직접 세면, 그럴듯한 함정 보기가 걸러진다.
🙋 학생 질문 — "튜터님, 결과를 예측하라는데 복잡한 쿼리는 어떻게 하나요?"
한 번에 다 보려 하지 마세요. 안쪽부터 차근차근 풀면 돼요. 인라인뷰가 있으면 그 안쪽 GROUP BY 결과를 먼저 그리고, 그다음 바깥 조건을 얹어요. 우리가 작은 더미 데이터(회원 12명·게시물 50개)로 연습한 게 바로 이 힘을 기르려는 거였어요. 행 수가 세 자리 이하면 결과를 손으로 셀 수 있으니까요. 복잡해 보여도 "안쪽 → 바깥쪽" 순서로 쪼개면 예측이 돼요.
Step 3: "1과목 모의고사 — 데이터 모델링의 이해"
1과목은 배점이 20점이지만, 과락선 8점이 걸려 있어 버릴 수 없어요. 다행히 이론 위주라 빠르게 득점할 수 있는 효율 구간이죠. B 카테고리에서 종이 위에 그렸던 정규화·식별자·관계·트랜잭션을 모의 문항으로 확인해 볼게요.
[문제 3] ★빈출 인스타 PostHashtag(post_id, hashtag_id)처럼 두 컬럼이 묶여 기본키(복합키)인 테이블이 있다. 어떤 컬럼이 복합키 전체가 아니라 그중 일부에만 종속될 때, 이 부분 함수 종속을 없애는 정규화 단계는?
① 제1정규형(1NF) ② 제2정규형(2NF) ③ 제3정규형(3NF) ④ BCNF ⑤ 제4정규형(4NF)
✅ 정답 보기
정답: ②
정규화는 1NF(컬럼을 원자값으로) → 2NF(부분 함수 종속 제거) → 3NF(이행 함수 종속 제거) 순서예요. "복합키의 일부에만 종속"이 바로 부분 함수 종속이라 제2정규형(2NF)에서 다뤄요. 이행 종속(3NF)과 헷갈리지 않게, "부분 종속 = 2NF"로 묶어 두세요.
[문제 4] ★빈출 인스타 회원 테이블의 기본키를, 주민등록번호 같은 업무 값 대신 1·2·3… 일련번호(시퀀스)로 새로 만들어 부여했다. 이 식별자에 대한 설명으로 옳은 것은?
① 본질 식별자다 ② 업무적으로 의미를 가진 식별자다 ③ 인조(대리) 식별자이며 업무 의미와 무관하게 부여한다 ④ 외래 식별자다 ⑤ 반드시 복합 식별자여야 한다
✅ 정답 보기
정답: ③
업무에서 유래한 값(주민번호·사번)을 그대로 쓰면 본질 식별자, 의미 없이 인위적으로 부여한 일련번호면 인조(대리) 식별자예요. 시퀀스로 만든 member_id가 전형적인 인조 식별자죠. 본질 식별자는 값이 바뀌거나 길어질 위험이 있어, 실무에선 인조 식별자를 기본키로 두는 경우가 많아요.
[문제 5] 한 회원은 여러 게시물을 올릴 수 있고, 한 게시물은 정확히 한 회원의 것이다. 회원(Member)과 게시물(Post)의 관계 차수(cardinality)는?
① 1:1 ② 1:N ③ N:1 ④ N:M ⑤ 관계 없음
✅ 정답 보기
정답: ②
회원 하나에 게시물 여럿, 게시물 하나엔 회원 하나 — 그래서 Member 1 : N Post예요. 참고로 회원↔회원의 Follow나 회원↔게시물의 Like는 양쪽이 여럿인 N:M이라, 중간에 연결 테이블을 둬서 풀었죠. 관계 차수는 "양쪽 각각에서 상대가 몇 개냐"로 읽으면 돼요.
[문제 6] 게시물을 올릴 때 'post 행 삽입'과 'hashtag 연결 삽입'이 함께 일어난다. 둘 중 하나만 성공하고 끝나면 안 되고, "둘 다 되거나 둘 다 안 되거나"여야 한다. 이를 보장하는 트랜잭션의 성질은?
① 원자성(Atomicity) ② 일관성(Consistency) ③ 고립성(Isolation) ④ 지속성(Durability) ⑤ 정규성(Normality)
✅ 정답 보기
정답: ①
"전부 반영되거나 전부 취소되거나(All or Nothing)"가 원자성이에요. 트랜잭션의 네 성질 ACID는 원자성·일관성·고립성·지속성이고, ⑤ 정규성은 ACID에 없는 가짜 보기예요. 보기에 그럴듯한 이름을 섞어 헷갈리게 하는 전형적인 함정이죠.
💡 한 줄 정리
1과목 모델링은 이론 10문항(20점)이지만 과락선 8점이 걸려 있다. 정규화·식별자·관계 차수·ACID는 빠르게 확보하는 효율 구간이다.
🙋 학생 질문 — "튜터님, 1과목은 20점뿐인데 꼭 챙겨야 하나요?"
배점만 보면 작아 보이지만, 과락 때문에 못 버려요. 10문항 중 4문항(8점)을 못 맞히면 과락으로 탈락이에요. 다행히 1과목은 SQL 계산이 없는 이론이라, 정규화 단계·식별자 종류·관계 차수·ACID만 정리해 두면 짧은 시간에 4문항 이상은 안정적으로 확보할 수 있어요. 효율로 보면 오히려 가성비 좋은 구간이에요.
Step 4: "2과목 모의고사 ① — SQL 기본 (함수·NULL·집계·조인)"
이제 80점이 걸린 2과목이에요. 전반부는 C 카테고리에서 배운 단일행 함수·NULL 처리·집계·조인이에요. 결과를 직접 세는 문제가 쏟아지니, 실행 순서와 NULL을 늘 의식하면서 풀어 보세요.
[문제 7] post 1의 caption은 '첫 게시물입니다 #안녕 #일상 #첫게시물'이다. 다음 쿼리의 결과는?
-- sql/queries/E2_mock_exam.sql
SELECT INSTR(caption, '#') FROM post WHERE post_id = 1;
① 8 ② 9 ③ 10 ④ 11 ⑤ 0
✅ 정답 보기
정답: ③
INSTR은 찾는 문자가 처음 나오는 위치를 1부터 세서 돌려줘요. '첫(1) (2)게(3)시(4)물(5)입(6)니(7)다(8) (9)#(10)…' — 공백도 한 글자라 첫 #은 10번째예요. 못 찾으면 0을 주지만, 여기선 있으니 위치 10. ⑤ 0은 "못 찾았을 때"와 헷갈리게 한 함정이에요.
[문제 8] 회원 12명 중 bio가 NULL인 회원이 4명이다. 다음 쿼리의 결과 건수는?
SELECT COUNT(*) FROM member WHERE NVL(bio, '비공개') = '비공개';
① 0 ② 4 ③ 8 ④ 12 ⑤ 오류
✅ 정답 보기
정답: ②
NVL(bio, '비공개')는 bio가 NULL이면 '비공개'로 바꿔 줘요. 그 결과가 '비공개'와 같은 행은, 원래 bio가 NULL이던 4명뿐이죠.
⚠️ 만약
WHERE bio = '비공개'나WHERE bio = NULL로 짰다면 결과는 0건이에요. NULL은=로 비교할 수 없어서, NULL을 다룰 땐NVL이나IS NULL을 써야 해요.
[문제 9] 회원을 회원번호의 짝/홀로 분류한다. 회원번호가 1~12일 때, 짝수 회원 수는?
SELECT DECODE(MOD(member_id, 2), 0, '짝수', '홀수') AS parity, COUNT(*)
FROM member
GROUP BY DECODE(MOD(member_id, 2), 0, '짝수', '홀수');
① 짝수 5 ② 짝수 6 ③ 짝수 7 ④ 짝수 12 ⑤ 오류
✅ 정답 보기
정답: ②
MOD(member_id, 2)가 0이면 짝수예요. 1~12 중 2·4·6·8·10·12 — 여섯 명이죠. DECODE는 값이 같은지를 보고 갈래를 나누는 함수라, 0이면 '짝수', 아니면 '홀수'로 라벨을 붙여요.
[문제 10] ★빈출 "게시물을 6개 이상 올린 회원"을 찾는다. 회원별 게시물 수는 1번 7개, 2번 7개, 3번 6개, 4번 6개, 9번 6개이고 나머지는 5개 이하다. 결과 행 수는?
SELECT member_id, COUNT(*)
FROM post
GROUP BY member_id
HAVING COUNT(*) >= 6;
① 4 ② 5 ③ 6 ④ 9 ⑤ 오류
✅ 정답 보기
정답: ②
7·7·6·6·6 — 회원 1·2·3·4·9 다섯 명이에요. 집계 결과(COUNT(*))에 거는 조건은 WHERE가 아니라 HAVING이에요.
⚠️
WHERE COUNT(*) >= 6으로 짜면 오류가 나요.WHERE는 그룹으로 묶기 전 개별 행을 거르는 단계라, 아직 만들어지지도 않은 집계값을 쓸 수 없거든요.
[문제 11] ★빈출 회원 12명, 게시물 50개인데 게시물이 0개인 회원이 3명(7·8·10번) 있다. 다음 두 쿼리의 결과 건수는?
-- (가) INNER JOIN
SELECT COUNT(*) FROM member m INNER JOIN post p ON m.member_id = p.member_id;
-- (나) LEFT JOIN
SELECT COUNT(*) FROM member m LEFT JOIN post p ON m.member_id = p.member_id;
① 50, 50 ② 50, 53 ③ 53, 53 ④ 53, 50 ⑤ 50, 62
✅ 정답 보기
정답: ②
INNER JOIN은 양쪽이 매칭되는 50건만 남겨요. LEFT JOIN은 왼쪽(회원)을 기준으로, 게시물이 없는 회원 3명도 오른쪽을 NULL로 채워 남기죠. 그래서 50 + 3 = 53이에요.
⚠️ 게시물 0개인 회원은
INNER JOIN에선 흔적도 없이 사라져요. "0개도 포함"이라는 말이 보이면OUTER JOIN을 떠올리세요.
💡 한 줄 정리
2과목 전반은 함수·NULL·집계·조인이다. COUNT는 컬럼을 넣으면 NULL을 빼고 세고, 집계 조건은 HAVING으로 걸며, OUTER JOIN은 짝 없는 쪽도 남긴다.
🙋 학생 질문 — "튜터님, 함수·집계·조인이 한 문제에 섞여 나오면 어디부터 보나요?"
쿼리의 실행 순서대로 읽으면 길이 보여요. 먼저 FROM과 JOIN으로 "어떤 행들이 모이는지" 행의 모양을 잡고, 다음 WHERE로 행을 거르고, GROUP BY로 묶고, HAVING으로 그룹을 거른 뒤, 마지막에 SELECT로 무엇을 보여줄지 정해요. 우리가 C 카테고리에서 다진 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY 순서가 그대로 무기가 돼요.
Step 5: "2과목 모의고사 ② — SQL 활용 (집합·서브쿼리)"
여기서부터가 2과목 후반, 합격선을 가르는 빈출 구간이에요. D 카테고리에서 배운 집합 연산과 서브쿼리부터 풀어 볼게요. 특히 NULL이 서브쿼리에 끼었을 때의 함정은 매 회차 나오니 꼭 잡아 두세요.
[문제 12] "좋아요가 달린 게시물 목록"(중복 없이 13개)과 "댓글이 달린 게시물 목록"(중복 없이 9개)을 합친다. 두 목록은 9개가 겹친다. 다음 두 쿼리의 결과 건수는?
-- sql/queries/E2_mock_exam.sql
-- (가) UNION
SELECT DISTINCT post_id FROM post_like
UNION
SELECT DISTINCT post_id FROM post_comment;
-- (나) UNION ALL
SELECT DISTINCT post_id FROM post_like
UNION ALL
SELECT DISTINCT post_id FROM post_comment;
① 13, 13 ② 13, 22 ③ 22, 22 ④ 22, 13 ⑤ 9, 13
✅ 정답 보기
정답: ②
UNION은 두 결과를 합치되 중복을 제거해요. 13개와 9개를 합쳐도, 겹치는 9개를 한 번만 세니 13이죠(댓글 달린 9개가 전부 좋아요도 받은 게시물이라 그래요). UNION ALL은 중복을 지우지 않고 그냥 이어 붙여서 13 + 9 = 22예요. 둘의 차이가 곧 "겹친 9개"예요.
[문제 13] 회원 4번은 1·2번을 팔로우하지만, 4번을 팔로우하는 사람은 2번뿐이다. "4번의 짝사랑(내가 팔로우하지만 나를 안 따라준 사람)"을 구하는 쿼리의 결과는?
SELECT following_id FROM follow WHERE follower_id = 4
MINUS
SELECT follower_id FROM follow WHERE following_id = 4;
① 1 ② 2 ③ 1, 2 ④ 결과 없음 ⑤ 2, 4
✅ 정답 보기
정답: ①
"내가 팔로우한 사람" {1, 2}에서 "나를 팔로우한 사람" {2}를 빼면 {1}만 남아요. 2번은 서로 맞팔이라 빠지고, 1번이 짝사랑이죠. MINUS(차집합)는 방향이 있어서, A MINUS B와 B MINUS A는 다른 결과예요.
[문제 14] ★빈출 댓글 테이블에서 parent_comment_id(대댓글의 부모)는 루트 댓글이면 NULL이다. 다음 쿼리의 결과 건수는?
SELECT COUNT(*) FROM post_comment
WHERE parent_comment_id NOT IN (SELECT parent_comment_id FROM post_comment);
① 전체 댓글 수 ② 루트 댓글 수 ③ 0 ④ 대댓글 수 ⑤ 오류
✅ 정답 보기
정답: ③
서브쿼리 결과에 NULL이 하나라도 섞이면, NOT IN은 어떤 행도 참이 되지 못해 공집합(0건)이 돼요. parent_comment_id엔 루트 댓글의 NULL이 들어 있으니, 통째로 0이 나오죠.
⚠️
NOT IN+ NULL = 0은 SQLD 최고 단골 함정이에요. 서브쿼리에 NULL이 있을 수 있으면NOT EXISTS를 쓰거나IS NOT NULL로 걸러야 해요.
[문제 15] 게시물 50개 중 좋아요가 한 번이라도 달린 게시물은 13개다. 다음 쿼리의 결과 건수는?
SELECT COUNT(*) FROM post p
WHERE NOT EXISTS (SELECT 1 FROM post_like l WHERE l.post_id = p.post_id);
① 13 ② 37 ③ 50 ④ 0 ⑤ 9
✅ 정답 보기
정답: ②
NOT EXISTS는 "좋아요가 하나도 없는 게시물"을 골라요. 50개 중 좋아요 받은 13개를 빼면 37개죠. EXISTS로 바꾸면 반대로 13이 나와요. 같은 NULL 상황에서도 NOT EXISTS는 [문제 14]의 NOT IN과 달리 공집합 함정이 없어요.
💡 한 줄 정리
집합은 UNION(중복 제거)·UNION ALL(그대로)·MINUS(방향 있는 차집합)로 갈린다. 서브쿼리는 NOT IN + NULL이 공집합 함정이고, NOT EXISTS가 안전하다.
🙋 학생 질문 — "튜터님, NOT IN이랑 NOT EXISTS는 같은 거 아니에요?"
서브쿼리에 NULL이 없을 땐 둘이 같은 결과를 줘요. 그래서 평소엔 헷갈리죠. 그런데 서브쿼리 결과에 NULL이 끼는 순간 갈려요. NOT IN은 NULL 하나에 통째로 0건이 되지만, NOT EXISTS는 "있냐 없냐"만 따지므로 NULL의 영향을 받지 않아요. 그래서 "NULL이 섞일 수 있는 컬럼"을 다룰 땐 NOT EXISTS가 안전한 선택이에요.
Step 6: "2과목 모의고사 ③ — SQL 활용 (계층형·윈도우·PIVOT)"
빈출 4대 토픽의 핵심인 계층형 질의와 윈도우 함수예요. 변별력이 가장 높은 구간이라, 여기서 합격이 갈려요. 댓글 트리, 좋아요 순위, 팔로워 증감 — 우리가 D 카테고리에서 만든 데이터로 풀어 봐요.
[문제 16] post 1의 댓글은 루트 3개에서 시작해, 가장 깊은 갈래가 "댓글 → 대댓글 → 대대댓글 → 대대대댓글"로 4단계까지 내려간다. 다음 쿼리의 MAX(LEVEL)은?
-- sql/queries/E2_mock_exam.sql
SELECT MAX(LEVEL)
FROM post_comment
START WITH parent_comment_id IS NULL AND post_id = 1
CONNECT BY PRIOR comment_id = parent_comment_id;
① 1 ② 2 ③ 3 ④ 4 ⑤ 7
✅ 정답 보기
정답: ④
LEVEL은 계층형 질의가 깊이를 매겨 주는 의사 컬럼이에요. 루트가 1단계고, 한 단계 내려갈 때마다 1씩 늘어요. 가장 깊은 갈래가 4단계니 MAX(LEVEL)은 4예요. ⑤ 7은 post 1의 전체 댓글 수라, "깊이"와 "개수"를 헷갈리게 한 함정이에요.
[문제 17] ★빈출 좋아요 수가 6·5·4·3·3·2·2·2…인 게시물을 좋아요 내림차순으로 순위 매긴다. 좋아요 3개인 게시물 2개가 공동 4위다. 바로 다음, 좋아요 2개 게시물의 RANK()와 DENSE_RANK() 값은?
SELECT post_id, COUNT(*) AS likes,
RANK() OVER (ORDER BY COUNT(*) DESC) AS rnk,
DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) AS drnk
FROM post_like
GROUP BY post_id;
① RANK 5, DENSE 5 ② RANK 6, DENSE 5 ③ RANK 5, DENSE 6 ④ RANK 6, DENSE 6 ⑤ RANK 4, DENSE 4
✅ 정답 보기
정답: ②
공동 4위가 둘이면, RANK()는 그 다음을 5가 아니라 6으로 건너뛰어요(1, 2, 3, 4, 4, 6). DENSE_RANK()는 건너뛰지 않고 바로 5를 줘요(1, 2, 3, 4, 4, 5). 동점 다음을 어떻게 매기느냐가 둘을 가르는 핵심이에요.
⚠️ "공동 N위 다음 순위"가 보이면 RANK(건너뜀)와 DENSE_RANK(안 건너뜀)를 구분하세요. 단골 출제 포인트예요.
[문제 18] 회원 1의 월별 팔로워가 1월 100 → 2월 108 → 3월 120 → 4월 110 → 5월 130이다. 다음 쿼리에서 4월 행의 growth 값은?
SELECT stat_month, follower_count,
follower_count - LAG(follower_count) OVER (ORDER BY stat_month) AS growth
FROM account_monthly_stat
WHERE member_id = 1;
① 10 ② -10 ③ 110 ④ NULL ⑤ 20
✅ 정답 보기
정답: ②
LAG은 같은 정렬 안에서 직전 행의 값을 끌어와요. 4월 행에서 직전은 3월(120)이니, 110 - 120 = -10이에요. 팔로워가 줄어든 달이라 음수가 나오죠. 참고로 1월은 직전 행이 없어 growth가 NULL이에요.
[문제 19] 좋아요 Top-3 게시물(실제 1~3위 post 12·1·40)을 올바르게 뽑는 방법은?
① GROUP BY 결과에 곧바로 WHERE ROWNUM <= 3을 건다
② ORDER BY로 정렬한 인라인뷰를 WHERE ROWNUM <= 3으로 감싼다
③ WHERE ROWNUM <= 3 … ORDER BY likes DESC 순으로 쓴다
④ ORDER BY만 쓰고 행 제한은 생략한다
⑤ HAVING ROWNUM <= 3을 쓴다
✅ 정답 보기
정답: ②
ROWNUM은 ORDER BY보다 먼저 매겨지므로, 먼저 정렬을 끝낸 인라인뷰를 만들고 그 바깥에서 ROWNUM으로 잘라야 해요. ①·③은 정렬 전에 잘려서 [문제 2]처럼 엉뚱한 행이 나와요. 같은 일을 FETCH FIRST 3 ROWS ONLY로 쓰면 더 짧고 함정도 없어요.
💡 한 줄 정리
빈출 4대의 핵심 — 계층형은 LEVEL로 깊이를 매기고, 윈도우는 RANK(건너뜀)·DENSE_RANK(안 건너뜀)의 동점 처리와 LAG의 직전 행, Top-N은 정렬을 먼저 끝내야 한다.
🙋 학생 질문 — "튜터님, 윈도우 함수는 종류가 너무 많아요. 시험엔 뭐가 자주 나오나요?"
전부 외우려 하지 말고, 단골 몇 개에 집중하세요. 순위 함수에서는 RANK와 DENSE_RANK의 동점 처리 차이가 가장 많이 나오고, 위치를 옮겨 보는 LAG/LEAD로 증감을 계산하는 유형도 단골이에요. 그리고 모든 윈도우 함수의 뼈대인 OVER (PARTITION BY … ORDER BY …) 구조, 즉 "어떤 그룹 안에서 어떤 순서로 보느냐"를 읽을 줄 알면 대부분 풀려요.
Step 7: "방언·함정 종합 모의 — 시험 단골 트랩"
지난 시간에 정리한 Oracle 사투리와 ANSI 표준어, 그리고 그 사이에 숨은 함정을 모아 풀어 볼게요. 방언 문제는 "결과는 같은데 문법이 다른 것"을 분별하는 눈을 묻는 경우가 많아요.
[문제 20] ★빈출 Oracle 고유 문법과 ANSI 표준의 짝이 잘못 연결된 것은?
① NVL ↔ COALESCE ② DECODE ↔ CASE ③ (+) ↔ OUTER JOIN ④ MINUS ↔ EXCEPT ⑤ ROWNUM ↔ DISTINCT
✅ 정답 보기
정답: ⑤
ROWNUM(행 개수 제한)의 표준 짝은 FETCH FIRST(또는 LIMIT)예요. DISTINCT는 중복을 없애는 문법이라 행 제한과는 전혀 다른 일을 해요. 나머지 네 쌍(NULL 채우기·갈래 나누기·외부 조인·차집합)은 모두 올바른 짝이에요.
[문제 21] bio가 NULL인 회원이 4명이다. 다음 두 쿼리의 결과 건수는?
-- sql/queries/E2_mock_exam.sql
-- (가) DECODE
SELECT COUNT(*) FROM member WHERE DECODE(bio, NULL, 'Y') = 'Y';
-- (나) CASE
SELECT COUNT(*) FROM member WHERE (CASE WHEN bio = NULL THEN 'Y' END) = 'Y';
① 4, 4 ② 4, 0 ③ 0, 4 ④ 0, 0 ⑤ 4, 12
✅ 정답 보기
정답: ②
DECODE는 특이하게 NULL과 NULL을 "같다"고 봐서, bio가 NULL인 4명을 'Y'로 매칭해요. 반면 CASE의 bio = NULL은 일반 비교라 NULL과는 절대 참이 되지 않아 0이에요.
⚠️ DECODE만 NULL = NULL을 매칭한다는 점이 함정이에요. CASE로 NULL을 잡으려면
WHEN bio IS NULL로 써야 해요.
[문제 22] Oracle의 (+) 외부 조인 연산자에 대한 설명으로 옳지 않은 것은?
① LEFT/RIGHT OUTER JOIN을 표현할 수 있다
② (+)만으로 FULL OUTER JOIN도 표현할 수 있다
③ 한 쿼리에서 양쪽에 동시에 (+)를 쓸 수 없다
④ ANSI OUTER JOIN보다 가독성이 떨어진다는 평가가 있다
⑤ Oracle 고유 문법이라 다른 DBMS로 그대로 옮기기 어렵다
✅ 정답 보기
정답: ②
(+)는 FULL OUTER JOIN을 못 해요. 양쪽에 (+)를 동시에 붙이는 게 금지라, 양쪽 모두 빠짐없이 남기는 FULL은 표현할 수 없어요. FULL이 필요하면 ANSI FULL OUTER JOIN을 써야 하죠. 이게 (+)의 대표적인 한계예요.
[문제 23] 게시물 0개인 회원 3명을 포함해 "회원별 게시물(없으면 NULL)"을 뽑는다. 다음 두 쿼리의 결과 건수는? (전체 53건)
-- (가) Oracle (+)
SELECT COUNT(*) FROM member m, post p WHERE m.member_id = p.member_id(+);
-- (나) ANSI LEFT JOIN
SELECT COUNT(*) FROM member m LEFT JOIN post p ON m.member_id = p.member_id;
① 50, 53 ② 53, 50 ③ 53, 53 ④ 50, 50 ⑤ 53, 62
✅ 정답 보기
정답: ③
두 방언은 같은 LEFT OUTER JOIN이라 결과가 똑같이 53이에요. (+)는 "NULL로 채울 쪽"인 post 컬럼에 붙여요. 방언만 다를 뿐 하는 일은 같다는 걸 보여 주는 문제예요. 결과는 같아도, 어느 게 Oracle 고유고 어느 게 표준인지는 분별할 줄 알아야 해요.
💡 한 줄 정리
방언 5쌍(NVL/COALESCE·DECODE/CASE·(+)/OUTER·MINUS/EXCEPT·ROWNUM/FETCH)은 결과가 같다. 단 DECODE의 NULL 매칭과 (+)의 FULL OUTER 불가는 단골 함정이다.
🙋 학생 질문 — "튜터님, 방언 문제는 둘 다 결과가 같다는데 왜 헷갈리게 내나요?"
결과가 같아도, 시험은 "어느 게 Oracle 고유고 어느 게 표준인지"를 분별하는지 물어요. 실무에서 다른 데이터베이스로 옮길 때 이 분별이 필요하거든요. 그래서 다섯 쌍의 짝은 외워 두되, 동시에 "두 방언이 같은 결과를 낸다"는 점도 함께 기억하세요. 거기에 더해, DECODE의 NULL 매칭이나 (+)의 FULL 불가처럼 "짝은 비슷한데 한쪽만 가진 특성"이 함정으로 나와요.
Step 8: "과락을 막는 마지막 30분 — 합격 전략과 체크리스트"
마지막은 시험장에서 바로 쓰는 전략이에요. 아는 걸 다 풀고도 시간 관리와 과락에서 무너지면 아깝잖아요. 합격을 지키는 세 가지를 정리할게요.
먼저 과락 방어예요. 합격은 총점 60점에 더해, 1과목 8점·2과목 32점이라는 최소선을 둘 다 넘어야 해요. 자신 있는 과목에 다 쏟다가 약한 과목이 과락선 아래로 떨어지면, 총점이 높아도 탈락이에요. 그래서 약한 과목도 "과락선까지는" 반드시 확보하는 분산이 먼저예요.
다음은 시간 관리예요. 한 문항에 막혀 오래 붙들지 마세요. 🌟 아는 것부터 빠르게 풀고, 막히는 건 표시해 둔 뒤 일단 넘기는 게 정석이에요. 객관식 5지선다는 빈칸으로 두면 0점이지만 찍으면 20% 기대값이 있으니, 끝까지 못 풀어도 빈칸 없이 채우세요.
마지막은 함정 체크리스트예요. 시험 직전에 새 문법을 보기보다, 그동안 틀렸던 함정 유형을 한 번 훑는 게 효율적이에요.
시험 직전 1분 체크리스트
NULL =NULL 금지 · NOT IN+NULL은 0 · COUNT(컬럼)은 NULL 제외
실행 순서 WHERE→GROUP BY→HAVING→SELECT→ORDER BY · ROWNUM은 정렬 전
동점 순위 RANK 건너뜀(1,2,2,4) · DENSE_RANK 안 건너뜀(1,2,2,3)
방언 NVL/COALESCE · DECODE/CASE · (+)/OUTER · MINUS/EXCEPT
차집합 MINUS는 방향이 있다 (A-B ≠ B-A)
이 다섯 줄이 오늘 푼 23문항의 함정을 거의 다 덮어요. 시험장에 들어가기 전 마지막으로 이 체크리스트만 떠올려도, 같은 실수를 반복하지 않아요.
💡 한 줄 정리
과락(1과목 8점·2과목 32점)을 먼저 막고 총점 60점을 넘긴다. 시간이 부족하면 아는 것부터 풀고 빈칸 없이 채우며, 단골 함정 체크리스트로 마무리한다.
🙋 학생 질문 — "튜터님, 시험 직전 10분엔 뭘 보면 좋을까요?"
새로운 걸 욱여넣지 마세요. 직전엔 위 체크리스트처럼 "내가 자주 틀린 함정 목록"을 훑는 게 가장 효율적이에요. NOT IN + NULL이 0이 되는 것, ROWNUM이 정렬보다 먼저 매겨지는 것, RANK와 DENSE_RANK의 동점 차이 — 이런 걸 마지막에 한 번 더 새기면, 시험장에서 같은 함정에 두 번 빠지지 않아요. 모르는 걸 새로 외우기보다, 아는 걸 안 틀리게 지키는 시간이에요.
마무리
오늘은 새 문법 대신, 그동안 쌓은 SQL을 시험장의 50문항으로 풀어내는 실전 감각을 만들었어요. 시험의 지도를 펼치고, 오답 보기를 거르는 눈을 기르고, 1과목 모델링부터 2과목의 함수·집계·조인·집합·서브쿼리·계층형·윈도우·방언까지 — 인스타그램 한 스키마 위에서 23문항을 직접 풀었죠.
오늘 배운 핵심 세 가지
- 💡 시험의 지도 — 50문항 90분, 총점 60점에 과목별 40% 과락. 2과목(80점)에 시간을 몰되 1과목 과락(8점)도 막는다.
- 💡 결과 예측의 힘 — 객관식은 쿼리 결과를 미리 세어 함정 보기를 거르는 게임이다. 실행순서·NULL·정렬·동점이 단골이다.
- 💡 전 범위 한 바퀴 — 모델링부터 윈도우·방언까지, 같은 인스타 데이터로 다 풀어 보며 약한 구간을 확인했다.
다음 시간 예고
이걸로 SQLD 대비는 한 바퀴를 다 돌았어요. 그런데 지금까지 짠 SQL은 전부 Oracle 방언이었죠. 다음 시간엔 같은 인스타그램 스키마를 통째로 MySQL 8.4로 옮겨 봐요. 지난 시간에 Oracle과 ANSI 표준어를 나란히 놨던 그 눈으로, 이번엔 Oracle과 MySQL을 견줘요. 시퀀스가 AUTO_INCREMENT로, ROWNUM이 LIMIT으로, NVL이 IFNULL로 바뀌는 — 같은 일을 또 다른 방언으로 적는 감각을 만들어요.
과제
오늘 푼 모의 문항을 직접 손으로 다뤄 보는 과제예요. 쿼리를 실행하기 전에 결과를 먼저 머릿속으로 예측한 뒤, 실행해서 맞혀 보세요.
[기초] 결과를 예측하고 검산하기
(가) SELECT COUNT(*), COUNT(DISTINCT member_id) FROM post; 의 두 값을 먼저 예측한 뒤 실행해 맞춰 보고, 왜 두 값이 다른지 한 줄로 적어 보세요. (나) [문제 10]의 HAVING COUNT(*) >= 6을 >= 5로 바꾸면 회원이 몇 명이 될지 예측하고 실행해 확인하세요. (다) [문제 8]을 WHERE bio = NULL로 바꾸면 결과가 몇 건일지 예측하고, 왜 NVL이나 IS NULL이 필요한지 한 줄로 정리하세요.
[응용] 함정 쿼리를 직접 다루기
(가) "좋아요 Top-3"를 ROWNUM으로 올바르게 짜고(인라인뷰에서 먼저 정렬), 틀린 버전([문제 2])과 결과를 나란히 비교하세요. (나) "회원 5번의 짝사랑"을 MINUS로 구하세요. (다) [문제 15]의 NOT EXISTS(좋아요 없는 게시물 37개)를, 같은 결과를 내는 NOT IN으로 바꿔 보세요. post_id엔 NULL이 없어 이번엔 NOT IN도 안전한데, 만약 NULL이 섞인다면 왜 깨지는지 한 줄로 적으세요.
[심화] 나만의 모의 문항 출제하기
(가) 인스타 스키마로, 오답 보기 네 개에 함정을 심은 5지선다 SQL 문항을 직접 하나 출제하고 정답과 해설을 쓰세요. (나) 그 문항이 오답 보기 4대 패턴(실행순서·NULL·정렬·방언) 중 무엇을 노렸는지 밝히세요. (다) 가능하면 동료와 문항을 바꿔 풀어 보고, 서로의 함정에 걸렸는지 확인하세요.
생각해볼 주제
1. 자격증 시험은 왜 "결과 예측"을 객관식으로 물을까
실무에선 쿼리를 실행해 결과를 바로 확인할 수 있어요. 그런데 SQLD는 실행 없이 머릿속으로 결과를 예측하게 하죠. 실행 환경이 있는데도 굳이 "읽어서 맞히는" 능력을 묻는 이유가 뭘까요? SQL을 직접 짤 줄 아는 것과, 남이 짠 SQL을 읽고 결과를 예측하는 것이 실무에서 각각 어떤 상황에 필요한지 떠올려 보세요.
2. 과락 제도는 합리적일까
총점이 충분해도 한 과목이 40% 미만이면 떨어져요. 한 분야를 아주 깊게 아는 사람과, 모든 분야를 골고루 아는 사람 중 자격증은 누구를 "자격자"로 보려는 걸까요? SQLD가 모델링과 SQL을 둘 다 과락 없이 요구하는 게 실무에서 어떤 의미를 갖는지, 그리고 이 제도가 불리하게 작용하는 사람은 없을지 함께 생각해 보세요.
3. 시험용 SQL과 실무 SQL은 같을까 다를까
시험 문제는 작은 데이터에 정답이 딱 하나예요. 실무는 수백만 행에 성능·인덱스까지 얽히죠. "시험을 잘 보는 것"과 "실무에서 SQL을 잘 쓰는 것"은 얼마나 겹치고 얼마나 다를까요? 오늘 푼 결과 예측·함정 분별 능력이 실무에서도 쓸모가 있을지, 아니면 시험만을 위한 기술인지 가늠해 보세요.
✅ 예시 답안정답 보기
과제와 생각해볼 주제의 예시답안이에요. 먼저 스스로 풀어 본 뒤, 막힌 부분만 펼쳐서 확인하세요. SQL은 직접 실행해 결과를 맞혀 보는 게 가장 좋아요.
과제 예시답안
🎯 [과제 기초 예시답안] 결과를 예측하고 검산하기
채점 포인트
| 항목 | 핵심 | 배점 |
|---|---|---|
| (가) | COUNT(*)=50, COUNT(DISTINCT member_id)=9 — 작성자 중복을 이해 |
40% |
| (나) | HAVING >= 5 → 7명 (회원 1·2·3·4·6·9·12) |
30% |
| (다) | bio = NULL → 0건, NULL은 =로 비교 불가 |
30% |
풀이 예시
(가) 게시물은 50개지만 작성자는 9명이에요. 한 회원이 여러 게시물을 올리니, member_id를 중복 없이 세면 작성자 수로 줄어들죠.
SELECT COUNT(*), COUNT(DISTINCT member_id) FROM post;
-- 결과: 50, 9
COUNT(*)는 게시물 행 전체(50)를, COUNT(DISTINCT member_id)는 서로 다른 작성자(9)를 세요. 게시물 0개인 회원 3명(7·8·10)은 post 테이블에 행이 없어 작성자 9명에도 안 들어가요.
(나) 조건을 6에서 5로 낮추면, 게시물 5개인 회원(6번·12번)이 새로 들어와 7명이 돼요.
SELECT member_id, COUNT(*)
FROM post GROUP BY member_id HAVING COUNT(*) >= 5;
-- 결과: 회원 1, 2, 3, 4, 6, 9, 12 (7명)
(다) WHERE bio = NULL은 0건이에요. NULL은 "값이 없음"이라 =로 같은지 비교할 수 없거든요. 그래서 NULL을 다룰 땐 NVL(bio, …)로 채우거나 WHERE bio IS NULL로 걸러야 해요.
SELECT COUNT(*) FROM member WHERE bio = NULL; -- 0 (틀린 방식)
SELECT COUNT(*) FROM member WHERE bio IS NULL; -- 4 (올바른 방식)
💡 튜터의 한마디 — 세 문제 모두 "NULL과 중복"이라는 2과목 단골 주제예요. COUNT가 컬럼을 받으면 NULL을 빼고, DISTINCT는 중복을 빼며, NULL은
=로 못 비교한다는 세 가지를 한 묶음으로 기억하세요.
🎯 [과제 응용 예시답안] 함정 쿼리를 직접 다루기
채점 포인트
| 항목 | 핵심 | 배점 |
|---|---|---|
| (가) | 인라인뷰에서 먼저 정렬 → Top-3 = 12·1·40 (틀린 버전은 1·5·2) | 40% |
| (나) | 회원 5번 짝사랑 = 회원 6 (MINUS 방향) |
30% |
| (다) | NOT IN 버전 = 37, NULL이 섞이면 공집합으로 깨짐 |
30% |
풀이 예시
(가) 올바른 Top-3은 인라인뷰 안에서 먼저 정렬한 뒤 바깥에서 ROWNUM으로 잘라야 해요.
-- 올바른 버전: 정렬 먼저
SELECT post_id, likes FROM (
SELECT post_id, COUNT(*) AS likes
FROM post_like GROUP BY post_id
ORDER BY COUNT(*) DESC, post_id
) WHERE ROWNUM <= 3;
-- 결과: post 12, 1, 40
틀린 버전([문제 2])은 ROWNUM을 정렬 전에 걸어 post 1·5·2가 나와요. 진짜 1등 post 12가 빠지는 게 핵심이에요.
(나) 회원 5번은 6번을 팔로우하지만, 6번은 5번을 안 따라줘요. 그래서 짝사랑은 6번이에요.
SELECT following_id FROM follow WHERE follower_id = 5
MINUS
SELECT follower_id FROM follow WHERE following_id = 5;
-- 결과: 6
(다) post_id엔 NULL이 없어 이번엔 NOT IN도 안전하게 37이 나와요.
SELECT COUNT(*) FROM post
WHERE post_id NOT IN (SELECT post_id FROM post_like);
-- 결과: 37
다만 만약 서브쿼리의 post_id에 NULL이 하나라도 섞이면, NOT IN은 어떤 행도 참이 못 돼 0으로 깨져요. 그럴 가능성이 있으면 NOT EXISTS가 안전하죠.
💡 튜터의 한마디 —
ROWNUM은 정렬보다 먼저,MINUS는 방향이 있고,NOT IN은 NULL에 약하다 — 세 함정이 한 과제에 다 들어 있어요. 실제로 손으로 짜 보면 보기만 읽을 때보다 훨씬 단단하게 박혀요.
🎯 [과제 심화 예시답안] 나만의 모의 문항 출제하기
채점 포인트
| 항목 | 핵심 | 배점 |
|---|---|---|
| (가) | 함정이 분명한 5지선다 문항 + 정답 + 해설 | 50% |
| (나) | 노린 오답 패턴(실행순서·NULL·정렬·방언)을 정확히 지목 | 30% |
| (다) | 동료와 교환 풀이로 함정 검증 | 20% |
풀이 예시
정답이 정해진 문항이 아니라, 좋은 출제의 예시를 보여 줄게요. 아래는 NULL 패턴을 노린 예시 문항이에요.
[예시 문항] 회원 12명 중 bio가 NULL인 회원이 4명이다. 다음 쿼리의 결과는?
SELECT AVG(LENGTH(bio)) FROM member;
① bio 12명의 평균 ② bio 8명의 평균(NULL 4명 제외) ③ NULL ④ 0 ⑤ 오류
정답: ② — AVG는 NULL을 빼고 계산해요. 그래서 분모가 12가 아니라 bio가 있는 8명이에요.
(나) 이 문항이 노린 패턴은 NULL이에요. "평균이니 당연히 12명으로 나누겠지"라는 착각을 유도하죠. ①이 바로 그 함정 보기예요. 집계 함수가 NULL을 빼고 센다는 걸 모르면 ①을 고르게 돼요.
(다) 동료와 바꿔 풀면, 내가 심은 함정이 실제로 통하는지 확인할 수 있어요. 동료가 ①에 걸렸다면 함정이 잘 작동한 거고, 너무 쉽게 ②를 골랐다면 보기를 더 그럴듯하게 다듬으면 돼요.
💡 튜터의 한마디 — 직접 출제해 보면 출제자의 시선이 생겨요. "어떤 착각을 유도할까"를 고민하는 순간, 같은 함정에 내가 안 빠지게 되죠. 좋은 함정은 진짜 답과 한 끗 차이라는 걸 기억하세요.
생각해볼 주제 예시답안
🤔 [생각해볼 주제 1] 자격증 시험은 왜 "결과 예측"을 객관식으로 물을까
문제 상황 요약
실무에선 쿼리를 실행해 결과를 바로 보는데, SQLD는 실행 없이 머릿속으로 결과를 예측하게 한다. 실행 환경이 있는데도 "읽어서 맞히는" 능력을 묻는 이유는 무엇일까.
튜터의 가이드 및 해설
SQL을 짜는 능력과 읽는 능력은 달라요. 짜는 건 빈 화면에서 시작하지만, 실무에서 더 자주 마주치는 일은 "남이 짜 둔 쿼리를 읽고 이해하는 것"이에요. 동료의 코드를 리뷰하거나, 오래된 쿼리를 고치거나, 장애 난 쿼리의 원인을 찾을 때 — 실행해 보기 전에 "이 쿼리가 무엇을 반환할지"를 읽어 낼 수 있어야 하죠.
결과를 예측한다는 건 곧 SQL의 동작 원리를 안다는 뜻이에요. ROWNUM이 언제 매겨지는지, NULL이 비교에서 어떻게 행동하는지를 몸으로 알아야 결과를 그릴 수 있거든요. 실행은 결과만 알려주지만, 예측은 "왜 그 결과인지"까지 알아야 가능해요. 시험이 객관식으로 결과를 묻는 건, 바로 이 원리 이해를 검증하려는 거예요.
🎯 SQLD는 이렇게 나온다
쿼리 한 개와 보기 다섯 개를 주고 "결과는?"을 묻는 게 가장 흔한 형태예요. 보기엔 늘 "올바르게 동작했다면 나올 결과"가 미끼로 섞여 있어요. 실행 순서·NULL·정렬을 따져 실제 결과를 세지 않으면 그 미끼에 걸려요. 결과 예측은 시험 전체를 관통하는 핵심 기술이에요.
💡 실무에선
코드 리뷰에서 "이 쿼리 결과가 의도와 맞나"를 실행 전에 읽어 내는 사람이 신뢰를 얻어요. 특히 운영 DB에선 함부로 실행해 볼 수 없는 경우가 많아, 읽고 예측하는 능력이 곧 안전이에요.
🤔 [생각해볼 주제 2] 과락 제도는 합리적일까
문제 상황 요약
총점이 충분해도 한 과목이 40% 미만이면 떨어진다. 한 분야를 깊게 아는 사람과 골고루 아는 사람 중, 자격증은 누구를 자격자로 보려는 걸까.
튜터의 가이드 및 해설
과락은 "최소한의 균형"을 요구하는 장치예요. SQLD가 인정하려는 건 "SQL 한 가지만 잘하는 사람"이 아니라 "데이터를 다룰 줄 아는 사람"이에요. 데이터를 다루려면 모델링(데이터가 어떻게 설계됐는지)과 SQL(그걸 어떻게 조회하는지)이 둘 다 필요하죠. 한쪽이 완전히 비면, 실무에서 반쪽짜리가 되기 쉬워요.
물론 과락이 불리하게 작용하는 사람도 있어요. SQL 실무 경험은 많지만 모델링 이론을 안 봐서 1과목에서 무너지는 경우죠. 그래서 우리가 1과목을 "배점은 작아도 과락선은 지키는" 효율 구간으로 잡은 거예요. 과락 제도는 "특정 분야의 빵점"을 막아 자격증의 신뢰를 지키는 대신, 약한 분야를 최소한은 메우라고 요구하는 셈이에요.
🎯 SQLD는 이렇게 나온다
1과목 모델링은 10문항(20점)이라 통째로 버리고 싶은 유혹이 있어요. 하지만 과락선 8점(4문항) 때문에 버릴 수 없죠. 출제 범위가 정규화·식별자·관계·트랜잭션으로 좁고 이론 위주라, 짧게 정리하면 4문항 이상은 안정적으로 확보돼요. "버리지 말고 최소선만 챙기는" 전략이 정답이에요.
💡 실무에선
조회는 잘하는데 테이블 설계를 못 하면, 성능 문제나 데이터 중복을 스스로 진단하지 못해요. 반대로 설계만 알고 SQL이 약하면 만든 데이터를 활용하지 못하죠. 과락이 요구하는 균형은 실무의 최소 요건과 닮아 있어요.
🤔 [생각해볼 주제 3] 시험용 SQL과 실무 SQL은 같을까 다를까
문제 상황 요약
시험 문제는 작은 데이터에 정답이 하나다. 실무는 수백만 행에 성능·인덱스까지 얽힌다. "시험을 잘 보는 것"과 "실무에서 SQL을 잘 쓰는 것"은 얼마나 겹치고 얼마나 다를까.
튜터의 가이드 및 해설
겹치는 부분이 생각보다 커요. 시험에서 기른 "결과 예측"과 "함정 분별"은 실무의 뼈대예요. NOT IN에 NULL이 끼면 결과가 0이 된다는 걸 아는 사람은, 실무에서 같은 실수로 빈 결과를 받고 헤매지 않아요. 실행 순서를 읽을 줄 알면, 의도와 다른 결과가 나왔을 때 어디가 문제인지 빠르게 짚죠.
다른 부분도 분명해요. 시험엔 없는 게 실무엔 있어요. 수백만 행에서의 성능, 인덱스 설계, 실행 계획, 동시에 여러 사용자가 접근할 때의 처리 — 이런 건 SQLD 범위 밖이에요. 시험은 "SQL을 정확히 읽고 쓰는 기본기"를 검증하고, 실무는 그 기본기 위에 "규모와 성능"을 얹는 거예요. 그래서 시험 합격은 출발선이지 결승선이 아니에요. 기본기가 탄탄할수록 그 위에 성능과 설계를 쌓기 쉬워지죠.
🎯 SQLD는 이렇게 나온다
시험은 의도적으로 작은 데이터(회원 12명·게시물 50개 수준)를 써요. 결과를 손으로 셀 수 있어야 "원리를 아는지"를 검증할 수 있거든요. 성능·인덱스는 묻지 않아요. 그래서 시험 대비는 "결과를 정확히 예측하는 정확성"에 집중하는 게 맞아요.
💡 실무에선
같은 쿼리도 데이터가 커지면 성능이 문제가 돼요. 시험에서 다진 정확성 위에, 실무에선 인덱스·실행 계획·튜닝을 더 배워 얹게 돼요. 기본기가 정확할수록 그다음 단계가 수월해진다는 점에서, 시험 공부는 결코 헛되지 않아요.