쿼리 한 줄을 종이 위에서 실행해 결과 표를 그려 내는 장이다. 문법 암기가 아니라 실행 순서가 답을 만든다
왜 이걸 하나 — 2회에서 가장 약했던 묶음이 SQL 이다. 이 장이 그 5~15점을 되찾는 첫 장이다.
11회분 220문항 중 SQL 조회는 13문항이다. 11회 중 8회에 나왔고, 2026년 2회에는 3문항이 나왔다. 정의·조작·제어(E09)와 DB 이론(E10)까지 합치면 SQL·DB 묶음이 39문항, 18%다.
| 꼴 | 몇 문항 | 무엇을 적나 | 보기 |
|---|---|---|---|
| 실행 결과 — 행 수 · 값 하나 | 8 | COUNT 의 값 하나 | 2024-1회 18번 · 2024-3회 3번 · 2025-3회 11번 · 12번 · 2026-1회 9번 · 13번 · 2026-2회 11번 · 18번 |
| 실행 결과 — 표 | 3 | 열 이름과 행을 표 꼴로 | 2023-3회 8번 · 2024-1회 13번 · 2025-1회 7번 |
| 문법 빈칸 | 1 | LIKE 패턴 · DESC | 2026-2회 6번 |
| 쿼리 작성 | 1 | SELECT 문 한 줄 전부 | 2023-1회 16번 |
→ 13문항 중 11문항이 「실행 결과」다. 문법을 아는 것과 결과를 정확히 세는 것은 다른 일이고, 틀리는 곳은 거의 언제나 NULL · 중복 · AND/OR 우선순위 · 열 이름 누락이다.
E01 진단의 SQL 4문항 중 3문항이 이 장 것이었다. 2회에서 SQL 이 샜다면 그 원인은 문법이 아니라 표를 손으로 돌리는 절차가 없었기 때문일 가능성이 크다. 이 장은 그 절차를 03절에서 한 줄씩 세운다.
문항 수는 이 노트의 문제 데이터(`_제작/기출/*.json`)에서 ch=E08 인 것을 센 값이다(2026-09-14). 회차별 복원본은 grandlife.co.kr, 2023~2026년 1회 문항은 이전 자료의 복원을 옮긴 것이라 문항 번호가 복원본마다 다를 수 있다.
왜 이걸 하나 — 시험 전날 이 절만 다시 본다. 답안에 적는 표기까지 여기 있다.
왜 이걸 하나 — 답이 왜 그 값인지 밑에서부터 세워야, 표가 바뀌어도 같은 절차로 푼다.
SELECT 문은 위에서 아래로 읽히지 않는다. 데이터베이스는 먼저 FROM 으로 표를 집고, WHERE 로 행을 버리고, GROUP BY 로 묶고, HAVING 으로 묶음을 버리고, 그제야 SELECT 의 열을 고르고, 마지막에 ORDER BY 로 줄을 세운다. 그래서 손으로 풀 때도 이 순서로 표를 깎아야 중간 행 수가 맞는다.
2026년 2회 6번의 학생 표(5행)에 아래 쿼리를 돌리는 과정을 단계별로 세어 보자.
| 단계 | 무엇을 하나 | 남는 것 |
|---|---|---|
| FROM 학생 | 표를 집는다 | 5행 |
| WHERE 이름 LIKE '이%' | 이 로 시작하는 이름만 | 이순신(3) · 이상(4) · 이정재(3) → 3행 |
| GROUP BY 학년 | 학년별로 묶는다 | 3학년 묶음(2행) · 4학년 묶음(1행) → 묶음 2개 |
| HAVING COUNT(*) >= 2 | 2행 미만 묶음을 버린다 | 3학년 묶음만 → 1묶음 |
| SELECT 학년, COUNT(*) | 열을 고른다 | 3, 2 |
| ORDER BY 학년 DESC | 줄을 세운다 | 한 행이라 그대로 |
→ WHERE 와 HAVING 의 차이가 여기서 보인다. WHERE 는 행을 거르고 HAVING 은 묶음을 거른다. 그래서 HAVING 에는 COUNT · AVG 같은 집계가 올 수 있고 WHERE 에는 못 온다.
별칭도 같은 이유로 갈린다. AS 인원 은 SELECT 단계에서 붙는 이름이라 그보다 먼저 도는 WHERE 나 HAVING 에서는 존재하지 않는다. 문제에 HAVING 인원 >= 2 처럼 적혀 있다면 그 DBMS 가 허용하는지 여부와 별개로, 답안에는 HAVING COUNT(*) >= 2 로 적는 것이 안전하다.
두 표를 잇는 원리는 하나다. 먼저 모든 행의 짝을 만들고(m × n), ON 조건이 참인 짝만 남긴다. 이것이 INNER JOIN 이고, FROM 에 두 표를 쉼표로 놓고 WHERE 에 등호를 쓴 것도 같은 결과다.
OUTER 는 거기에 한 가지를 더한다. 짝을 못 찾은 행을 버리지 않고 상대편 열을 NULL 로 채워 남긴다. LEFT 는 왼쪽 표의 행을, RIGHT 는 오른쪽 표의 행을, FULL 은 양쪽을 남긴다.
2026년 2회 18번의 A(3행) · B(4행)로 RIGHT OUTER JOIN 의 결과를 그리면 이렇다.
| A.id | A.v | B.id | B.w | 어떻게 생겼나 |
|---|---|---|---|---|
| 1 | 10 | 1 | 100 | B 의 1 에 A 의 첫 행이 짝 |
| 1 | 20 | 1 | 100 | B 의 1 에 A 의 둘째 행도 짝 — B 행이 두 번 나온다 |
| 2 | 30 | 2 | 200 | 짝 하나 |
| NULL | NULL | 3 | 300 | A 에 3 이 없어 A 쪽이 NULL |
| NULL | NULL | 4 | 400 | A 에 4 가 없어 A 쪽이 NULL |
→ 5행이다. B 의 4행보다 많은 이유는 id 1 이 A 에 두 번 있어서고, 여기에 WHERE A.id IS NULL 을 걸면 아래 2행만 남아 답이 2 가 된다. 행 수를 셀 때는 한쪽에 같은 키가 여러 번 있는지를 먼저 본다.
같은 표로 LEFT 를 돌리면 A 의 3행이 각각 짝을 찾아 3행이고, INNER 도 3행이다. CROSS 는 조건 없이 3 × 4 = 12행이다. 2025년 3회 11번의 CROSS JOIN … WHERE A.NAME LIKE B.RULE 은 이 12행 같은 곱을 만든 뒤 LIKE 로 거르는 꼴이다.
NULL 은 0 도 빈 문자열도 아니다. 그래서 x > NULL 이나 x = NULL 은 참도 거짓도 아닌 모른다(UNKNOWN)가 되고, WHERE 는 참인 행만 통과시키므로 그 행은 빠진다. NULL 인 행을 잡으려면 IS NULL 뿐이다.
집계에서는 규칙 세 개가 전부다. COUNT(*) 는 행 자체를 세니 NULL 도 센다. COUNT(열) · SUM · AVG · MAX · MIN 은 그 열이 NULL 인 행을 계산에서 아예 뺀다. 맞는 행이 하나도 없으면 COUNT 는 0 이지만 AVG · SUM 은 NULL 이다.
2025년 3회 12번이 이 세 규칙을 한 문제에 담았다. 표 5행에 WHERE COL1 IN (2, 3) OR COL2 IN (3, 5) 는 5행을 다 통과시키지만, COUNT(COL2) 는 COL2 가 NULL 인 첫 행을 빼서 4 다. COUNT(*) 였다면 5 다.
세 번째 규칙이 상관 서브쿼리와 만나면 함정이 된다. 2026년 2회 11번에서 x = 10 인 행은 「자기보다 작은 x」가 없어 안쪽 AVG 가 NULL 이 되고, 10 > NULL 이 통과하지 못해 그 행이 빠진다. 「빈 집합의 평균은 0」이라고 생각하면 답이 4 로 틀린다.
서브쿼리는 세 꼴뿐이다. 값 하나를 돌려주면 = · > 로 비교하고, 여러 값이면 IN 으로 집합에 드는지 보고, 바깥 행의 값을 안에서 쓰면 상관 서브쿼리라 바깥 행마다 안쪽을 다시 계산한다. 어느 꼴이든 푸는 순서는 같다 — 가장 안쪽을 값으로 바꾸고, 그 값을 바깥에 넣는다.
2024년 3회 3번은 서브쿼리가 3겹이다. 가장 안쪽 GROUP BY project_id HAVING COUNT(*) < 2 → 20. 그 위 SELECT name … WHERE project_id IN (20) → Beta. 바깥 JOIN 에서 p.name IN ('Beta') → Charlie 1행. 세 겹이라도 한 겹씩 값으로 바꾸면 한 줄짜리 문제가 된다.
상관 서브쿼리는 표로 푼다. 바깥 행을 한 줄씩 놓고, 그 행의 값으로 안쪽을 계산한 결과를 옆 칸에 적고, 조건이 참인지를 마지막 칸에 적는다. 2026년 2회 11번을 이 표로 풀면 x = 10 · 20 · 30 · 40 네 줄에 AVG 가 NULL · 10 · 13.3 · 18.75 로 적히고, 참인 줄이 3개다.
KIND = 'A' AND SCORE >= 80 OR KIND = 'C' 는 (A 이면서 80 이상) 또는 (C) 다. 2024년 1회 18번의 답 1 은 이 우선순위에서 나왔고, 괄호를 옮긴 훈련 1번은 0 이 된다.왜 이걸 하나 — 03절의 절차를 기출 13문항과 예상 2문항에 그대로 대 본다. 회차순이라 꼴이 어떻게 반복되는지도 보인다.
푸는 법은 하나다. 표를 종이에 옮기고, 실행 순서대로 행을 지워 가며 남는 행 수를 적고, 마지막에 SELECT 열만 골라 답 칸에 옮긴다. 머릿속으로만 세지 않는다 — 2회에서 샌 점수는 거기서 나왔을 가능성이 가장 크다.
성적 테이블에서 과목이름별 최소점수·최대점수를 구하되, 평균 점수가 90 이상인 과목만 조회하는 SQL을 작성하시오. (컬럼 별칭: 최소점수, 최대점수)
SELECT 과목이름, MIN(점수) AS 최소점수, MAX(점수) AS 최대점수 FROM 성적 GROUP BY 과목이름 HAVING AVG(점수) >= 90;다음 두 테이블과 SQL문의 실행 결과를 순서대로 쓰시오.
| T1.A |
|---|
| 1 |
| 2 |
| 3 |
| 3 |
| T2.A |
|---|
| 2 |
| 4 |
다음 SQL을 실행한 결과를 쓰시오.
SAMPLE
| A | B |
|---|---|
| 1 | a |
| 2 | b |
| 3 | c |
다음 SQL문의 실행 결과를 쓰시오.
RESULT
| ID | KIND | SCORE |
|---|---|---|
| 1 | A | 70 |
| 2 | B | 90 |
| 3 | A | 50 |
| 4 | C | 95 |
다음 테이블과 SQL문을 보고 출력값을 쓰시오.
employee
| employee_id | name | project_id |
|---|---|---|
| 1 | Alice | 10 |
| 2 | Bob | 10 |
| 3 | Charlie | 20 |
| 4 | Dana | 30 |
| 5 | Ethan | 30 |
project
| project_id | name |
|---|---|
| 10 | Alpha |
| 20 | Beta |
| 30 | Gamma |
다음 두 테이블과 SQL문을 보고 조회 결과를 테이블 형태로 쓰시오.
직원
| 직원번호 | 이름 |
|---|---|
| 101 | 이순신 |
| 102 | 강감찬 |
| 103 | 김유신 |
보상
| 직원번호 | 성과급 |
|---|---|
| 101 | 1000 |
| 102 | 300 |
| 103 | 450 |
다음 A, B 테이블과 SQL문을 보고 출력되는 행의 수를 쓰시오.
A 테이블
| NAME |
|---|
| Smith |
| Allen |
| Scott |
B 테이블
| RULE |
|---|
| S% |
| %T% |
다음 SQL문의 실행 결과를 쓰시오. 테이블 A의 데이터가 다음과 같을 때 조회 결과를 구하시오.
A
| COL1 | COL2 |
|---|---|
| 2 | NULL |
| 3 | 6 |
| 2 | 3 |
| NULL | 3 |
| 4 | 5 |
STUDENT 테이블(총 200명, 학과 3개)에서: ① SELECT COUNT(*) FROM STUDENT; ② SELECT COUNT(DISTINCT 학과) FROM STUDENT; ③ SELECT COUNT(DISTINCT 학과) FROM STUDENT WHERE 학과='전산과'; 각각의 결과는?
다음 두 테이블과 SQL문을 보고 반환되는 값을 쓰시오.
employee 테이블
| dep_id |
|---|
| 10 |
| 20 |
| 20 |
dept 테이블
| dept_id | budget |
|---|---|
| 10 | 100 |
| 20 | 300 |
| 30 | 200 |
다음 [학생] 테이블을 이용해 이름이 ‘이’로 시작하는 학생들을 이름 기준 내림차순으로 정렬하려고 한다. [쿼리]의 빈칸 ①과 ②에 들어갈 알맞은 내용을 쓰시오.
[학생]
| 학번 | 이름 | 학년 | 학과 |
|---|---|---|---|
| 202101 | 이순신 | 3 | 컴퓨터공학 |
| 202102 | 김영희 | 1 | 전기공학 |
| 202103 | 이상 | 4 | 건축공학 |
| 202104 | 임꺽정 | 2 | 전자공학 |
| 202105 | 이정재 | 3 | 토목공학 |
[쿼리]
% 는 0글자 이상 아무 글자다. 「이로 시작」이면 '이%', 「이로 끝」이면 '%이', 「이가 들어 있다」면 '%이%'. 내림차순은 DESC, 생략하면 ASC(오름차순)다. sqlite 로 돌리면 이정재 · 이순신 · 이상 순으로 3행이 나온다. 작은따옴표까지 적는 것이 안전하다. 복원 출처는 grandlife.co.kr 2026-2회 복원 풀이(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·수치가 실제와 다를 수 있다.다음 테이블과 SQL문을 보고 실행 결과를 쓰시오.
[A]
| id | x |
|---|---|
| 1 | 10 |
| 2 | 20 |
| 3 | 30 |
| 4 | 40 |
[B]
| id | y |
|---|---|
| 1 | 5 |
| 1 | 15 |
| 2 | 20 |
| 3 | 35 |
| 5 | 50 |
10 > NULL 은 참이 아니다. x=20 → id{1} → AVG(5,15)=10, 20>10 참. x=30 → id{1,2} → AVG(5,15,20)=13.3, 참. x=40 → id{1,2,3} → AVG(5,15,20,35)=18.75, 참. 3행. sqlite 로 돌려 3 을 확인했다. NULL 비교가 거짓이 되는 것이 이 문제의 함정이다. 복원 출처는 grandlife.co.kr 2026-2회 복원 풀이(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·수치가 실제와 다를 수 있다.다음 테이블과 SQL문의 실행 결과를 쓰시오.
[A]
| id | v |
|---|---|
| 1 | 10 |
| 1 | 20 |
| 2 | 30 |
[B]
| id | w |
|---|---|
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
| 4 | 400 |
WHERE A.id IS NULL 은 그 짝 없는 2행만 고른다. sqlite 3.49 로 돌려 2 를 확인했다. 복원 출처는 grandlife.co.kr 2026-2회 복원 풀이(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·수치가 실제와 다를 수 있다.성적 테이블이 다음과 같을 때 쿼리 결과를 쓰시오.
사원(사번, 이름, 부서코드): (1,김,A) (2,이,B) (3,박,NULL) / 부서(부서코드, 부서명): (A,영업) (B,개발)
① SELECT COUNT(*) FROM 사원 s INNER JOIN 부서 d ON s.부서코드 = d.부서코드;
② SELECT COUNT(*) FROM 사원 s LEFT JOIN 부서 d ON s.부서코드 = d.부서코드; 각각의 결과는?
왜 이걸 하나 — 아는 문제에서 깎이는 자리는 정해져 있다. 채점자의 눈으로 답안을 다시 본다.
| 자리 | 새는 꼴 | 적는 법 | 기출 |
|---|---|---|---|
| 결과의 꼴 | 행 수를 묻는데 값을 적는다, 값을 묻는데 행 수를 적는다 | 문제의 마지막 문장을 다시 읽는다 — 「행의 수」「반환되는 값」「결과를 쓰시오」「테이블 형태로」 | 2025-3회 11번 · 2025-1회 7번 |
| 열 이름 | 값만 적고 열 이름을 뺀다 | 「결과를 쓰시오」면 열 이름 줄부터 적는다. 별칭이 있으면 별칭이 열 이름이다 | 2024-1회 13번 · 2023-3회 8번 |
| 행 순서 | ORDER BY 를 무시하고 표 순서대로 적는다 | DESC 면 큰 것부터. ORDER BY 가 없으면 표 순서대로 적되, 순서가 채점 대상이 아닐 수 있다 | 2023-3회 8번 |
| NULL | NULL 을 0 으로 세거나, NULL 비교를 통과시킨다 | COUNT(열) · AVG 는 NULL 을 뺀다. 빈 집합 AVG 는 NULL 이고 비교는 통과 못 한다 | 2025-3회 12번 · 2026-2회 11번 |
| 중복 | UNION 에서 중복을 남기거나, RIGHT JOIN 에서 같은 키의 곱을 놓친다 | UNION 은 중복 제거, UNION ALL 은 유지. 한쪽에 같은 키가 n 개면 n 행이 된다 | 2023-3회 8번 · 2026-2회 18번 |
| AND · OR | 왼쪽부터 계산한다 | AND 를 먼저 괄호로 묶고 본다 | 2024-1회 18번 |
| 따옴표 · 대소문자 | LIKE 패턴에 따옴표를 빼거나, 키워드를 소문자로 적는다 | 문자열은 '이%' 처럼 작은따옴표까지, 키워드는 대문자 | 2026-2회 6번 |
| 빈칸 여러 개 | 첫 칸만 맞고 둘째 칸은 비워 둔다 | 소문항은 부분 점수가 있다. 모르는 칸도 가장 그럴듯한 것을 적는다 | 2026-2회 6번 · 2026-1회 9번 |
「결과를 쓰시오」 유형에서 열 이름을 빼는 것이 가장 흔한 감점이다. 채점자는 정답표의 표 꼴과 대조하므로, 열 이름 한 줄 + 값 줄을 기본 꼴로 삼고 행 수만 묻는 문제에서만 숫자 하나를 적는다.
왜 이걸 하나 — 기출과 같은 표에서 조건 하나만 바꾼 문제다. 답이 바뀌는 이유를 말할 수 있으면 원리가 손에 붙은 것이다.
5문항은 전부 04절의 표를 다시 쓴다. 괄호 위치, COUNT 의 괄호 안, JOIN 의 방향, LIKE 의 % 자리, 빈 집합 — 바뀐 것은 하나씩이고 답은 전부 바뀐다.
다음 테이블과 SQL문의 실행 결과를 쓰시오.
RESULT
| ID | KIND | SCORE |
|---|---|---|
| 1 | A | 70 |
| 2 | B | 90 |
| 3 | A | 50 |
| 4 | C | 95 |
다음 테이블과 SQL문의 실행 결과를 쓰시오.
A
| COL1 | COL2 |
|---|---|
| 2 | NULL |
| 3 | 6 |
| 2 | 3 |
| NULL | 3 |
| 4 | 5 |
다음 두 테이블과 SQL문의 실행 결과 행 수를 쓰시오.
A
| id | v |
|---|---|
| 1 | 10 |
| 1 | 20 |
| 2 | 30 |
B
| id | w |
|---|---|
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
| 4 | 400 |
다음 테이블과 SQL문의 실행 결과를 쓰시오.
학생
| 학번 | 이름 | 학년 | 학과 |
|---|---|---|---|
| 202101 | 이순신 | 3 | 컴퓨터공학 |
| 202102 | 김영희 | 1 | 전기공학 |
| 202103 | 이상 | 4 | 건축공학 |
| 202104 | 임꺽정 | 2 | 전자공학 |
| 202105 | 이정재 | 3 | 토목공학 |
다음 테이블과 SQL문의 실행 결과를 쓰시오.
A
| id | x |
|---|---|
| 1 | 10 |
| 2 | 20 |
| 3 | 30 |
| 4 | 40 |
파이썬에 sqlite 가 들어 있어 설치 없이 쿼리를 확인할 수 있다. 파워셸에서 아래를 그대로 치면 2026년 2회 18번의 답 2 가 찍힌다. 표와 쿼리만 바꿔 가며 손으로 센 값과 기계가 센 값을 맞춰 보는 것이 이 장의 훈련이다.
위 명령은 이 노트를 만든 컴퓨터의 파이썬 3.12(sqlite 3.49.1)에서 실행해 2 가 나온 것이다. RIGHT JOIN 은 sqlite 3.39 부터 지원한다. 이 장의 기출 13개 · 예상 2개 · 훈련 5개의 정답도 같은 방법으로 전부 돌려 확인했다(쿼리 작성 문항은 그 문장을 표본 표에 실행해 문법을 확인).
왜 이걸 하나 — 아래를 안 보고 적을 수 있어야 시험장에서 표를 돌릴 수 있다.