함수는 행마다 값 하나를 내는 단일행(문자·숫자·날짜·변환·NULL)과 여러 행을 하나로 접는 다중행(집계)으로 나뉜다. 윈도우 함수는 집계를 하되 행을 줄이지 않는다.
다중행 서브쿼리에는 IN·ANY·ALL·EXISTS 를 쓴다. > ANY 는 최솟값보다 크다, > ALL 은 최댓값보다 크다는 뜻이고, 서브쿼리가 비면 ALL 은 모든 행이 참이다.
그룹 묶음 수는 ROLLUP n + 1, CUBE 2ⁿ, GROUPING SETS 는 적은 개수 그대로다. 결과 행 수는 묶음마다 나오는 그룹 수를 더한 값이다.
동점에서 RANK 는 건너뛰고(1, 2, 2, 4), DENSE_RANK 는 건너뛰지 않고(1, 2, 2, 3), ROW_NUMBER 는 다른 번호를 준다. OVER 에 ORDER BY 만 있으면 범위는 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 다.
DELETE 는 DML 이라 되돌릴 수 있고, TRUNCATE·DROP 은 DDL 이라 Oracle 에서 바로 확정된다. 제약 조건은 PK·UNIQUE·NOT NULL·CHECK·FK 다섯 가지다.
W3~W7 에서 배운 2과목(SQL 기본 및 활용, 관리 구문)을 헷갈리는 짝 위주로 다시 놓는다. NULL·COUNT 함정은 W8 Day 1, 실행 순서·조인 함정은 W8 Day 2, Oracle 과 SQL Server 차이는 W8 Day 3 에 있어 여기서는 다루지 않는다.
Oracle 의 명시적 변환 함수는 이 셋이다(표준 CAST 도 쓸 수 있다). 암시적 변환은 NLS 설정에 따라 결과가 달라진다
NULL
NVL, NVL2, NULLIF, COALESCE
NVL2(a, b, c) 는 a 가 NULL 이 아니면 b. NULLIF(a, b) 는 같으면 NULL, 다르면 a
조건
CASE, DECODE
CASE 는 처음 참인 WHEN 하나만 쓴다. DECODE 는 NULL 끼리를 같다고 본다
다중행
COUNT, SUM, AVG, MAX, MIN
그룹마다 한 값. COUNT(*) 외에는 NULL 을 건너뛴다
숫자·문자 함수에서 부호와 자릿수가 섞이는 경우를 한 번에 돌린다.
SELECT SUBSTR('SQLDEVELOPER', -3, 2) AS SUB, INSTR('SQLDEVELOPER', 'E', 1, 2) AS INS, ROUND(45.678, -1) AS RND, TRUNC(45.678, 1) AS TRC, CEIL(-1.2) AS CL, FLOOR(-1.2) AS FL, MOD(-7, 3) AS MD FROM DUAL;
SU INS RND TRC CL FL MD-- ---------- ---------- ---------- ---------- ---------- ----------PE 7 50 45.6 -1 -2 -1
SUBSTR(…, -3, 2) 는 뒤에서 세 번째 글자 P 에서 두 글자다. INSTR(…, 'E', 1, 2) 는 두 번째 E 의 위치 7 이다.
ROUND(45.678, -1) 은 일의 자리에서 반올림해 50, TRUNC(45.678, 1) 은 소수 첫째 자리까지 남겨 45.6 이다.
음수에서 CEIL 은 0 쪽(−1), FLOOR 는 0 에서 먼 쪽(−2)이다. Oracle 의 MOD 는 결과 부호가 앞 인자를 따라 −1 이다.
30번 부서 급여(950, 1250, 1250, 1500, 1600, 2850)를 기준으로 세 연산자의 결과 행 수를 나란히 센다. 마지막 열은 없는 부서 99 를 기준으로 한 > ALL 이다.
SELECT (SELECT COUNT(*) FROM EMP WHERE SAL > ANY (SELECT SAL FROM EMP WHERE DEPTNO = 30)) AS GT_ANY, (SELECT COUNT(*) FROM EMP WHERE SAL > ALL (SELECT SAL FROM EMP WHERE DEPTNO = 30)) AS GT_ALL, (SELECT COUNT(*) FROM EMP WHERE SAL IN (SELECT SAL FROM EMP WHERE DEPTNO = 30)) AS IN_CNT, (SELECT COUNT(*) FROM EMP WHERE SAL > ALL (SELECT SAL FROM EMP WHERE DEPTNO = 99)) AS ALL_EMPTY FROM DUAL;
위아래 SELECT 의 열 개수(ORA-01789)와 자료형 계열(ORA-01790)이 맞아야 한다.
결과 열 이름은 첫 SELECT 가 정한다. ORDER BY 는 맨 끝에 한 번만 쓴다.
연산자끼리 우선순위가 같아 위에서 아래로 계산한다.
10번 부서 직무(CLERK, MANAGER, PRESIDENT)와 20번 부서 직무(CLERK 2, ANALYST 2, MANAGER 1)로 네 연산자의 행 수를 비교한다.
SELECT (SELECT COUNT(*) FROM (SELECT JOB FROM EMP WHERE DEPTNO = 10 UNION SELECT JOB FROM EMP WHERE DEPTNO = 20)) AS U, (SELECT COUNT(*) FROM (SELECT JOB FROM EMP WHERE DEPTNO = 10 UNION ALL SELECT JOB FROM EMP WHERE DEPTNO = 20)) AS UA, (SELECT COUNT(*) FROM (SELECT JOB FROM EMP WHERE DEPTNO = 10 INTERSECT SELECT JOB FROM EMP WHERE DEPTNO = 20)) AS I, (SELECT COUNT(*) FROM (SELECT JOB FROM EMP WHERE DEPTNO = 10 MINUS SELECT JOB FROM EMP WHERE DEPTNO = 20)) AS M FROM DUAL;
U UA I M---------- ---------- ---------- ---------- 4 8 2 1
UNION ALL 은 3 + 5 = 8행 그대로다. UNION 은 중복을 지워 CLERK·MANAGER·PRESIDENT·ANALYST 4행이다.
공통은 CLERK·MANAGER 2행, 10번에만 있는 직무는 PRESIDENT 1행이다.
행 수는 두 단계로 센다. 먼저 묶음 목록을 펼치고, 각 묶음에서 나오는 그룹 수를 더한다.
문법
펼친 묶음
묶음 수
ROLLUP(A, B)
(A, B), (A), ()
n + 1 = 3
CUBE(A, B)
(A, B), (A), (B), ()
2ⁿ = 4
GROUPING SETS(A, B)
(A), (B)
적은 대로 2
GROUPING SETS((A, B), ())
(A, B), ()
2
EMP 의 그룹 수는 (DEPTNO, JOB) 조합 9개, DEPTNO 3개, JOB 5개, 총계 1개다.
SELECT (SELECT COUNT(*) FROM (SELECT DEPTNO, JOB FROM EMP GROUP BY DEPTNO, JOB)) AS BASE, (SELECT COUNT(*) FROM (SELECT DEPTNO, JOB FROM EMP GROUP BY ROLLUP(DEPTNO, JOB))) AS RLP, (SELECT COUNT(*) FROM (SELECT DEPTNO, JOB FROM EMP GROUP BY CUBE(DEPTNO, JOB))) AS CUB, (SELECT COUNT(*) FROM (SELECT DEPTNO, JOB FROM EMP GROUP BY GROUPING SETS(DEPTNO, JOB))) AS GSETS FROM DUAL;
30번 부서를 급여 오름차순으로 세운다. MARTIN·WARD 가 1250 으로 동점이다. 마지막 열은 OVER 에 ORDER BY 만 준 누적 합이다.
SELECT ENAME, SAL, RANK() OVER (ORDER BY SAL) AS RNK, DENSE_RANK() OVER (ORDER BY SAL) AS DRNK, ROW_NUMBER() OVER (ORDER BY SAL, ENAME) AS RN, SUM(SAL) OVER (ORDER BY SAL) AS RUN_SUM FROM EMP WHERE DEPTNO = 30 ORDER BY SAL, ENAME;
동점 뒤 TURNER 의 RANK 는 4, DENSE_RANK 는 3 이다. ROW_NUMBER 는 ENAME 을 두 번째 키로 줘서 MARTIN 이 2, WARD 가 3 으로 고정되었다.
RUN_SUM 에서 MARTIN 과 WARD 가 둘 다 3450 이다. 기본 범위가 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 라서 같은 SAL 값인 행을 한꺼번에 더한다. 한 행씩 쌓으려면 ROWS 로 바꾼다(W6 Day 2).
OVER 안
계산 범위
비어 있음 OVER ()
전체 행
PARTITION BY 만
파티션 전체
ORDER BY 포함
파티션 처음부터 현재 행 값까지 (RANGE)
윈도우 함수는 SELECT·ORDER BY 절에만 쓴다. WHERE 에 쓰면 ORA-30483 이라 순위로 거르려면 인라인 뷰로 감싼다.
LAST_VALUE 가 (정렬값에 동점이 없으면) 현재 행 값을 돌려주는 것도 같은 기본 범위 때문이다. 동점이 있으면 같은 정렬값을 가진 마지막 행의 값이 나온다.
⚠️ 함정: 순위 함수 문항에서 "동점 다음 순위" 와 "마지막 순위" 를 따로 묻는다. RANK 의 마지막 값은 맨 뒤에 동점이 없으면 행 수, DENSE_RANK 의 마지막 값은 서로 다른 값의 개수다.
SELECT REGEXP_SUBSTR('A12-B345-C6', '[0-9]+', 1, 2) AS SUB, REGEXP_COUNT('A12-B345-C6', '[0-9]') AS CNT, REGEXP_INSTR('A12-B345-C6', '-') AS INS, REGEXP_REPLACE('2026-11-04', '(\d+)-(\d+)-(\d+)', '\3/\2/\1') AS REP FROM DUAL;
SUB CNT INS REP--- ---------- ---------- ----------345 6 4 04/11/2026
[0-9]+ 의 두 번째 일치는 345 다. [0-9] 는 숫자 한 글자라 개수는 6 이다.
LIKE 는 문자열 전체가 맞아야 하고 REGEXP_LIKE 는 일부만 맞아도 된다. 전체를 맞추려면 ^…$ 로 감싼다.
단일행 함수 : 행 하나에 결과 하나를 내는 함수. 문자·숫자·날짜·변환·NULL 함수가 여기에 든다.
다중행 서브쿼리 : 여러 행을 돌려주는 서브쿼리. IN·ANY·ALL·EXISTS 와 함께 쓴다.
그룹 묶음 : ROLLUP·CUBE·GROUPING SETS 를 펼쳤을 때 나오는 GROUP BY 기준 하나하나. () 는 총계 묶음이다.
기본 윈도우 범위 : OVER 에 ORDER BY 만 있을 때 쓰이는 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. 동점 행을 함께 포함한다.
역방향 전개 : CONNECT BY PRIOR MGR = EMPNO 처럼 자식에서 부모로 올라가는 계층형 질의.
암묵적 COMMIT : Oracle 에서 DDL 실행 전후로 자동으로 일어나는 확정.
📝 연습 문제
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
EMP 의 (DEPTNO, JOB) 조합은 9가지다. SELECT COUNT(*) FROM (SELECT DEPTNO, JOB, COUNT(*) FROM EMP GROUP BY GROUPING SETS ((DEPTNO, JOB), ())); 의 결과는?
문제 2
20번 부서의 급여는 SCOTT 3000, FORD 3000, JONES 2975, ADAMS 1100, SMITH 800 이다. 다음 SQL 에서 JONES 행의 RNK, DRNK 값은?
SELECT ENAME, RANK() OVER (ORDER BY SAL DESC) AS RNK, DENSE_RANK() OVER (ORDER BY SAL DESC) AS DRNK FROM EMP WHERE DEPTNO = 20;
문제 3
99번 부서는 EMP 에 없다. SELECT COUNT(*) FROM EMP WHERE SAL > ALL (SELECT SAL FROM EMP WHERE DEPTNO = 99); 의 결과는?
문제 4
Oracle 에서 DELETE·TRUNCATE·DROP 에 대한 설명으로 옳은 것은?
문제 5
다음 SQL 에서 KING 행의 LEVEL 값은? (SMITH 의 관리자는 FORD, FORD 의 관리자는 JONES, JONES 의 관리자는 KING 이고 KING 은 관리자가 없다)
SELECT LEVEL, ENAME FROM EMP START WITH ENAME = 'SMITH' CONNECT BY PRIOR MGR = EMPNO;