3단원 SQL 기본의 열두 레슨에서 한 문제씩, 모두 12문제입니다. 문제의 SQL은 레슨의 예제 데이터(DEPT·EMP·SALGRADE)로 Oracle에서 돌린다고 보고, 보기 넷 가운데 하나를 고른 뒤 '정답'을 펼쳐 맞춰 보세요.
해설은 정답의 까닭을 먼저 적고, 나머지 보기를 한 줄씩 짚습니다. 고른 보기의 줄부터 읽으면 어디서 생각이 갈렸는지 알 수 있습니다.
EMP의 MGR은 관리자의 사번입니다. SALGRADE는 월급이 LOSAL 이상 HISAL 이하이면 그 등급입니다.
EMP
| EMPNO | ENAME | SAL | DEPTNO | MGR |
|---|---|---|---|---|
| 1001 | 김민준 | 520 | 10 | NULL |
| 1002 | 이서연 | 380 | 20 | 1006 |
| 1003 | 박지호 | 410 | 20 | 1006 |
| 1004 | 최유나 | 450 | 30 | 1001 |
| 1005 | 정하늘 | 300 | NULL | 1004 |
| 1006 | 강도윤 | 470 | 20 | 1001 |
DEPT
| DEPTNO | DNAME | LOC |
|---|---|---|
| 10 | 기획 | 서울 |
| 20 | 개발 | 판교 |
| 30 | 영업 | 부산 |
| 40 | 인사 | 서울 |
SALGRADE
| GRADE | LOSAL | HISAL |
|---|---|---|
| 1 | 0 | 349 |
| 2 | 350 | 399 |
| 3 | 400 | 449 |
| 4 | 450 | 499 |
| 5 | 500 | 9999 |
문제 1
쇼핑몰의 회원 테이블 MEMBER는 회원번호를 기본키로 두고, 추천인번호는 같은 테이블의 회원번호를 가리키는 외래키로 둡니다. 옳은 것을 고르세요.
- ① 추천해 준 회원이 없으면 추천인번호를 비워 둘 수 없어서, 0 같은 값을 넣어야 합니다.
- ② 이미 있는 회원번호로 다시 넣는 INSERT도, 회원번호를 비운 INSERT도 막힙니다.
- ③ 추천인번호는 같은 MEMBER 테이블을 가리키므로 외래키로 만들 수 없습니다.
- ④
TRUNCATE TABLE MEMBER는 테이블의 행을 지우는 문장이라 DML에 속합니다.
정답
② — 기본키는 같은 값도 NULL도 막습니다. 이미 있는 회원번호는 ORA-00001, 비운 회원번호는 ORA-01400으로 거절됩니다.
- ① 외래키는 NULL을 받지 못한다고 봤습니다. 외래키는 가리킬 행이 없는 값만 막고 NULL은 받으므로, 추천인이 없으면 비워 둡니다. 0을 넣으면 회원번호 0인 회원이 없어 오히려 막힙니다(ORA-02291).
- ③ 외래키는 다른 테이블만 가리킨다고 봤습니다. EMP의 MGR이 같은 테이블의 EMPNO를 가리키듯, 외래키는 자기 테이블의 기본키도 가리킵니다.
- ④ 행을 지우는 일이라 DML로 봤습니다. TRUNCATE는 테이블을 통째로 비우는 DDL이고, 행을 골라 지우는 DELETE가 DML입니다.
다시 볼 레슨: 관계형 데이터베이스 개요
문제 2
김민준(1001)의 MGR은 NULL입니다. 다음 쿼리의 결과 A와 B로 옳은 것을 고르세요.
SELECT ENAME || '(' || MGR
|| ')' AS A,
SAL + MGR * 0 AS B
FROM EMP
WHERE EMPNO = 1001;
- ① A는 NULL, B는 520
- ② A는 NULL, B는 NULL
- ③ A는
김민준(), B는 520 - ④ A는
김민준(), B는 NULL
정답
④ — Oracle의 ||는 NULL을 건너뛰고 나머지 문자열만 잇습니다. 산술 연산에 NULL이 섞이면 결과는 NULL이라, MGR * 0도 0이 아니라 NULL이고 B도 NULL입니다.
- ① 두 연산을 거꾸로 봤습니다.
||는 NULL을 건너뛰어 A가김민준()이고, 산술 연산은 NULL이 섞이면 NULL이라 B가 NULL입니다. - ②
||도 NULL이 섞이면 NULL이 된다고 봤습니다. 그것은 SQL Server의 연결(기본 설정)이고, Oracle의||는 NULL을 건너뛰고 나머지 문자열만 잇습니다. - ③
MGR * 0을 0으로 봤습니다. NULL에 0을 곱해도 NULL이라SAL + NULL이 되고, B는 NULL입니다.
| A | B |
|---|---|
| 김민준() | NULL |
다시 볼 레슨: SELECT 문
문제 3
다음 식과 Oracle의 결과를 짝지은 것으로 옳지 않은 것을 고르세요.
- ①
ADD_MONTHS(DATE '2026-04-30', 1)→ 2026년 5월 30일 - ②
RTRIM('SQLQL', 'QL')→S - ③
TRUNC(5678.9, -2)→5600 - ④
LAST_DAY(TO_DATE('2026.02.10', 'YYYY.MM.DD'))→ 2026년 2월 28일
정답
① — 4월 30일은 4월의 말일입니다. ADD_MONTHS는 시작일이 그달의 말일이면 결과 달의 말일을 내므로, 한 달 뒤는 5월 31일입니다.
- ② 맞습니다. RTRIM의 둘째 인자 'QL'은 글자 묶음이라, 오른쪽 끝에서 Q나 L이 이어지는 동안 모두 지워 S만 남습니다.
- ③ 맞습니다. 자리 -2는 백의 자리까지 남기라는 뜻이고, TRUNC는 반올림 없이 버려 5600입니다.
- ④ 맞습니다. TO_DATE를 TO_CHAR처럼 날짜 → 문자로 보면 오류 같지만, TO_DATE는 문자 → 날짜입니다.
'2026.02.10'을 형식'YYYY.MM.DD'의 꼴대로 읽어 2026년 2월 10일이 되고, LAST_DAY는 그달의 마지막 날 28일을 냅니다.
SELECT TO_CHAR(ADD_MONTHS(
DATE '2026-04-30', 1),
'YYYY-MM-DD') AS A,
RTRIM('SQLQL', 'QL')
AS B,
TRUNC(5678.9, -2) AS C,
TO_CHAR(LAST_DAY(
TO_DATE('2026.02.10',
'YYYY.MM.DD')),
'YYYY-MM-DD') AS D
FROM DUAL;| A | B | C | D |
|---|---|---|---|
| 2026-05-31 | S | 5600 | 2026-02-28 |
다시 볼 레슨: 단일행 함수
문제 4
정하늘(1005)은 DEPTNO가 NULL이고 MGR이 1004입니다. 다음 쿼리의 결과 A·B·C로 옳은 것을 고르세요.
SELECT NVL2(DEPTNO, MGR, 0)
AS A,
DECODE(DEPTNO, NULL,
'Y', 'N') AS B,
CASE DEPTNO
WHEN NULL THEN 'Y'
END AS C
FROM EMP
WHERE EMPNO = 1005;
- ① 0, N, Y
- ② 0, Y, NULL
- ③ 1004, N, NULL
- ④ 1004, Y, Y
정답
② — NVL2는 첫 인자가 NULL이면 셋째 값을 내어 A는 0입니다. DECODE는 NULL과 NULL을 같다고 봐서 Y를 냅니다. 단순형 CASE는 DEPTNO = NULL로 견줘 참이 되지 않는데 ELSE도 없어서 C는 NULL입니다.
- ① DECODE와 단순형 CASE를 거꾸로 봤습니다. NULL끼리 같다고 보는 쪽은 DECODE라 B가 Y이고,
=로 견주는 단순형 CASE는 NULL을 찾지 못해 C가 NULL입니다. - ③ NVL2를 NVL처럼 봐서, 첫 인자가 NULL이면 둘째 값 MGR이 나온다고 봤습니다. NVL2는 NULL이 아닐 때 둘째, NULL일 때 셋째 값입니다. B도 DECODE가 NULL끼리 같다고 봐서 Y입니다.
- ④ NVL2의 둘째·셋째 값을 거꾸로 보고,
CASE DEPTNO WHEN NULL이 NULL을 찾는다고 봤습니다. A는 셋째 값 0이고, 단순형 CASE는 NULL을 찾지 못해 C가 NULL입니다.
| A | B | C |
|---|---|---|
| 0 | Y | NULL |
다시 볼 레슨: NULL 함수와 CASE
문제 5
다음 쿼리의 결과로 나오는 사원을 고르세요.
SELECT EMPNO,
ENAME
FROM EMP
WHERE ENAME LIKE '_도%'
OR DEPTNO <> 20
AND SAL < 400
ORDER BY EMPNO;
- ① 강도윤
- ② 강도윤, 정하늘
- ③ 결과 없음(0행)
- ④ 정하늘
정답
① — AND를 OR보다 먼저 계산해서, 조건은 아래처럼 묶입니다.
ENAME LIKE '_도%'
OR (DEPTNO <> 20
AND SAL < 400)
둘째 글자가 '도'인 강도윤은 앞 조건으로 남고, 괄호 안이 참인 행은 없습니다. 정하늘은 SAL < 400이 참이어도 NULL <> 20이 알 수 없음이라 AND 결과도 알 수 없음입니다.
- ②
NULL <> 20을 참으로 봤습니다. 정하늘의 DEPTNO는 NULL이라NULL <> 20은 알 수 없음이고, AND로 묶으면SAL < 400이 참이어도 알 수 없음이라 빠집니다. - ③ OR를 AND보다 먼저 묶었습니다. 그러면 SAL이 400보다 작아야 남는데, 그런 이서연·정하늘은 앞의 OR가 참이 아닙니다. AND가 먼저라 강도윤은 LIKE 하나로 남습니다.
- ④ OR를 먼저 묶고
NULL <> 20도 참으로 봤습니다. AND가 OR보다 먼저라 강도윤은 LIKE만으로 남고, 정하늘은NULL <> 20이 알 수 없음이라 빠집니다.
| EMPNO | ENAME |
|---|---|
| 1006 | 강도윤 |
다시 볼 레슨: WHERE 절
문제 6
다음 쿼리의 A와 B로 옳은 것을 고르세요.
SELECT AVG(CASE
WHEN DEPTNO = 20 THEN SAL
END) AS A,
AVG(CASE
WHEN DEPTNO = 20 THEN SAL
ELSE 0
END) AS B
FROM EMP;
- ① A 210, B 210
- ② A 210, B 420
- ③ A 420, B 210
- ④ A 420, B 420
정답
③ — A의 CASE는 20번 부서가 아닌 세 행에서 NULL을 내고, AVG는 NULL을 빼고 380·410·470의 평균 420을 냅니다. B는 ELSE 0 때문에 여섯 행 모두 값이 있어 1260 ÷ 6 = 210입니다.
- ① A도 여섯 행으로 나눈다고 봤습니다. CASE가 낸 NULL은 AVG가 건너뛰므로, A는 20번 세 사람만의 평균 420입니다.
- ② A와 B를 거꾸로 봤습니다. ELSE가 없는 A는 20번이 아닌 행이 NULL이라 빠져 420이고, ELSE 0인 B는 0까지 여섯 값으로 나눠 210입니다.
- ④ ELSE 0도 평균에서 빠진다고 봤습니다. 0은 NULL이 아니라 값이라, B는 1260을 여섯 행으로 나눈 210입니다.
| A | B |
|---|---|
| 420 | 210 |
다시 볼 레슨: GROUP BY와 집계 함수 · NULL 함수와 CASE
문제 7
다음 쿼리의 결과로 옳은 것을 고르세요.
SELECT COUNT(*) AS CNT,
SUM(SAL) AS TOTAL
FROM EMP
WHERE DEPTNO = 20
HAVING SUM(SAL) >= 1500;
- ① CNT가 3, TOTAL이 1260인 한 줄
- ② GROUP BY가 없어서 오류
- ③ CNT가 0, TOTAL이 NULL인 한 줄
- ④ 결과 없음(0행)
정답
④ — GROUP BY가 없으면 WHERE를 거친 3행 전체가 한 그룹이 됩니다. 그 합계 1260이 HAVING 조건에 맞지 않아 그룹째 빠지고, 결과는 0행입니다.
- ① HAVING을 건너뛰고 WHERE까지만 따라갔습니다. 3행을 묶은 한 그룹의 합계 1260이
HAVING SUM(SAL) >= 1500에서 걸러집니다. - ② HAVING에는 GROUP BY가 꼭 있어야 한다고 봤습니다. GROUP BY가 없으면 테이블 전체(여기서는 WHERE를 거친 3행)가 한 그룹이고, HAVING은 그 한 그룹을 거릅니다.
- ③ 남은 행이 없을 때의 결과와 섞었습니다. CNT 0 · TOTAL NULL 한 줄은 GROUP BY 없이 WHERE에서 행이 모두 빠졌을 때 나옵니다. 여기는 3행이 한 그룹을 이룬 뒤 HAVING이 그 그룹을 지웁니다.
다시 볼 레슨: HAVING 절
문제 8
Oracle에서 다음 쿼리 결과의 맨 위 두 줄로 알맞은 것을 고르세요.
SELECT ENAME,
MGR,
SAL
FROM EMP
ORDER BY 2 DESC, 3;
- ① 최유나, 강도윤
- ② 김민준, 박지호
- ③ 김민준, 이서연
- ④ 이서연, 박지호
정답
③ — 2는 SELECT 목록의 둘째 항목 MGR입니다. Oracle은 NULL을 가장 큰 값으로 보아 내림차순에서 MGR이 NULL인 김민준을 맨 앞에 둡니다. DESC는 MGR에만 걸리므로, MGR이 1006으로 같은 두 사람은 SAL 오름차순(380·410)으로 서서 이서연이 둘째입니다.
- ① DESC가 MGR에 걸리지 않는다고 봤습니다.
2 DESC의 DESC는 바로 앞 기준인 둘째 항목 MGR에 붙어, MGR이 큰 쪽부터 섭니다. - ② SAL에도 DESC가 걸린다고 봤습니다. DESC는 기준마다 따로 붙어서 뒤의
3(SAL)은 오름차순이고, MGR이 같은 두 사람 가운데 380인 이서연이 먼저입니다. - ④ SQL Server처럼 NULL을 가장 작은 값으로 봤습니다. Oracle은 NULL을 가장 큰 값으로 보아, 내림차순에서 MGR이 NULL인 김민준이 맨 앞입니다.
| ENAME | MGR | SAL |
|---|---|---|
| 김민준 | NULL | 520 |
| 이서연 | 1006 | 380 |
| 박지호 | 1006 | 410 |
| 정하늘 | 1004 | 300 |
| 최유나 | 1001 | 450 |
| 강도윤 | 1001 | 470 |
다시 볼 레슨: ORDER BY 절
문제 9
Oracle에서 오류가 나는 쿼리를 고르세요.
- ①
SELECT DEPTNO, SUM(SAL) AS TOTAL FROM EMP GROUP BY DEPTNO ORDER BY TOTAL; - ②
SELECT DEPTNO, SUM(SAL) FROM EMP GROUP BY DEPTNO ORDER BY SUM(SAL) DESC; - ③
SELECT DEPTNO, SUM(SAL) FROM EMP GROUP BY DEPTNO ORDER BY COUNT(*); - ④
SELECT DEPTNO, SUM(SAL) FROM EMP GROUP BY DEPTNO ORDER BY SAL;
정답
④ — ORDER BY는 GROUP BY 다음에 처리되어 그때의 줄은 부서 그룹입니다. 20번 그룹에는 SAL이 셋이라 정렬 기준을 정할 수 없어 ORA-00979 오류가 납니다.
- ① 별칭으로는 정렬할 수 없다고 봤습니다. ORDER BY는 SELECT보다 나중에 처리되어, SELECT에서 붙인 별칭 TOTAL로 정렬할 수 있습니다.
- ② 집계 함수로는 정렬할 수 없다고 봤습니다. 그룹마다 값이 하나인
SUM(SAL)은 GROUP BY를 쓴 쿼리의 ORDER BY에 올 수 있습니다. - ③ SELECT에 없는 것으로는 정렬할 수 없다고 봤습니다.
COUNT(*)도 그룹마다 값이 하나인 집계 함수라, SELECT에 없어도 ORDER BY에 씁니다.
다시 볼 레슨: SELECT 문이 실행되는 순서
문제 10
다음 쿼리 결과에서 40번 부서 줄의 A와 B로 옳은 것을 고르세요.
SELECT D.DEPTNO,
COUNT(*) AS A,
COUNT(E.EMPNO) AS B
FROM DEPT D
LEFT OUTER JOIN EMP E
ON D.DEPTNO = E.DEPTNO
GROUP BY D.DEPTNO
ORDER BY D.DEPTNO;
- ① A 0, B 0
- ② A 1, B 0
- ③ A 1, B 1
- ④ 40번 부서 줄은 결과에 없습니다.
정답
② — 사원이 없는 40번 부서도 LEFT OUTER JOIN으로 한 줄 남고, 그 줄의 EMP 쪽 칸은 NULL로 채워집니다. COUNT(*)는 그 줄을 세어 1이고, COUNT(E.EMPNO)는 NULL을 빼고 세어 0입니다.
- ①
COUNT(*)도 NULL로 채운 줄을 뺀다고 봤습니다.COUNT(*)는 칸이 아니라 행을 세므로, 외부 조인이 남긴 40번 줄 하나를 세어 1입니다. - ③
COUNT(E.EMPNO)도 줄을 센다고 봤습니다. 40번 줄의 E.EMPNO는 외부 조인이 채운 NULL이라, NULL을 건너뛰는COUNT(열)은 0입니다. - ④ 외부 조인을 내부 조인처럼 봤습니다. DEPT를 왼쪽에 둔 LEFT OUTER JOIN은 사원이 없는 40번 부서도 한 줄로 남깁니다.
| DEPTNO | A | B |
|---|---|---|
| 10 | 1 | 1 |
| 20 | 3 | 3 |
| 30 | 1 | 1 |
| 40 | 1 | 0 |
다시 볼 레슨: 조인 · GROUP BY와 집계 함수
문제 11
급여 등급 조인에서 BETWEEN의 아래 끝 조건만 남겼습니다. 다음 쿼리의 CNT로 옳은 것을 고르세요.
SELECT COUNT(*) AS CNT
FROM EMP E, SALGRADE S
WHERE E.SAL >= S.LOSAL;
- ① 1
- ② 6
- ③ 19
- ④ 30
정답
③ — HISAL 조건이 없어서 사원마다 LOSAL이 자기 월급 이하인 등급 모두와 이어집니다. 김민준(520) 5개, 이서연(380) 2개, 박지호(410) 3개, 최유나(450) 4개, 정하늘(300) 1개, 강도윤(470) 4개를 더해 19행입니다.
- ①
>=를=처럼 봤습니다. LOSAL과 같은 값만 이으면 최유나(450) 1행이지만,>=는 LOSAL이 자기 월급 이하인 등급을 모두 잇습니다. - ② 사원마다 등급이 하나씩 이어진다고 봤습니다. 그것은 HISAL까지 견주는 BETWEEN일 때이고, 아래 끝만 남기면 한 사원이 여러 등급과 이어집니다.
- ④ 조건이 없는 카티션 곱(6 × 5)으로 봤습니다.
E.SAL >= S.LOSAL이 맞지 않는 짝은 빠지므로 30보다 적습니다.
| CNT |
|---|
| 19 |
다시 볼 레슨: 조인 더 보기
문제 12
EMP_LOC은 사원이 실제로 일하는 도시(LOC)를 적은 테이블입니다. DEPT의 LOC(부서가 있는 도시)와 열 이름은 같지만 뜻이 다릅니다.
| EMPNO | DEPTNO | LOC |
|---|---|---|
| 1001 | 10 | 서울 |
| 1002 | 20 | 판교 |
| 1003 | 20 | 서울 |
| 1004 | 30 | 부산 |
다음 쿼리의 결과로 알맞은 것을 고르세요.
CREATE TABLE EMP_LOC (
EMPNO NUMBER,
DEPTNO NUMBER,
LOC VARCHAR2(20)
);
INSERT INTO EMP_LOC
VALUES (1001, 10, '서울');
INSERT INTO EMP_LOC
VALUES (1002, 20, '판교');
INSERT INTO EMP_LOC
VALUES (1003, 20, '서울');
INSERT INTO EMP_LOC
VALUES (1004, 30, '부산');
SELECT EMPNO,
DEPTNO,
LOC,
DNAME
FROM EMP_LOC
NATURAL JOIN DEPT
ORDER BY EMPNO;
- ① 3행
- ② 4행
- ③ 16행
- ④ LOC가 두 테이블에 다 있어 오류가 납니다(ORA-00918).
정답
① — NATURAL JOIN은 이름이 같은 열 DEPTNO와 LOC를 모두 조인 조건으로 씁니다. 1003은 일하는 도시가 서울이라 DEPT 20의 판교와 달라 빠지고, 3행이 남습니다.
- ② DEPTNO만으로 잇는다고 봤습니다. DEPTNO만 견주면 4행이지만, NATURAL JOIN은 뜻이 다른 LOC도 이름이 같아 조인 조건에 넣습니다.
- ③ 조인 조건이 없는 카티션 곱(4 × 4)으로 봤습니다. NATURAL JOIN은 이름이 같은 열로 조건을 만들어 값이 맞는 행끼리만 잇습니다.
- ④ 이름이 같은 LOC를 테이블 이름 없이 써서 오류라고 봤습니다. NATURAL JOIN에 쓰인 열은 두 테이블의 열이 하나로 합쳐져
LOC로만 쓰고, 오히려 앞에 테이블 이름이나 테이블 별칭을 붙이면 오류(ORA-25155)입니다.
| EMPNO | DEPTNO | LOC | DNAME |
|---|---|---|---|
| 1001 | 10 | 서울 | 기획 |
| 1002 | 20 | 판교 | 개발 |
| 1004 | 30 | 부산 | 영업 |
다시 볼 레슨: 표준 조인