SQLD를 공부하면서 함수 이름은 외웠는데, 막상 문제를 풀면 NULL 처리, COUNT(*)와 COUNT(컬럼)의 차이, RANK와 DENSE_RANK의 차이가 자꾸 헷갈렸다.
그래서 시험 직전에 빠르게 훑어볼 수 있도록 자주 출제되는 SQL 함수와 함정 포인트를 한 번에 정리해 보았다.
예제는 Oracle을 기준으로 작성했으며, SQL Server와 함수명이 다른 부분은 마지막에 따로 정리했다.
SQL 함수는 처리하는 행의 수에 따라 구분할 수 있다.
| 구분 | 특징 | 대표 함수 |
|---|---|---|
| 단일행 함수 | 한 행마다 하나의 결과 반환 | UPPER, SUBSTR, ROUND, NVL |
| 다중행 함수 | 여러 행을 묶어 하나의 결과 반환 | SUM, AVG, COUNT, MAX, MIN |
| 분석 함수 | 여러 행을 계산하지만 기존 행은 유지 | RANK, ROW_NUMBER, LAG |
-- 사원마다 결과가 출력되는 단일행 함수
SELECT UPPER(ename)
FROM emp;
-- 부서마다 하나의 결과가 출력되는 다중행 함수
SELECT deptno, AVG(sal)
FROM emp
GROUP BY deptno;
함수가 중첩되어 있으면 가장 안쪽 함수부터 실행된다.
| 함수 | 설명 | 결과 예시 |
|---|---|---|
UPPER('Sql') | 대문자로 변환 | SQL |
LOWER('Sql') | 소문자로 변환 | sql |
INITCAP('sql developer') | 단어 첫 글자를 대문자로 변환 | Sql Developer |
| 함수 | 설명 |
|---|---|
LENGTH(문자열) | 문자열의 글자 수 반환 |
LENGTHB(문자열) | 문자열의 바이트 수 반환 |
SUBSTR(문자열, 시작, 길이) | 문자열 일부 추출 |
INSTR(문자열, 찾을문자) | 문자의 위치 반환 |
SELECT SUBSTR('DATABASE', 1, 4),
INSTR('DATABASE', 'A')
FROM dual;
실행 결과는 다음과 같다.
DATA, 2
Oracle에서 문자열의 첫 번째 위치는 1이다. SUBSTR의 시작 위치가 음수이면 문자열 뒤에서부터 센다.
SELECT SUBSTR('DATABASE', -4, 4)
FROM dual;
-- 결과: BASE
| 함수 | 설명 |
|---|---|
LTRIM | 왼쪽의 공백 또는 지정 문자 제거 |
RTRIM | 오른쪽의 공백 또는 지정 문자 제거 |
TRIM | 양쪽의 공백 또는 지정 문자 제거 |
SELECT LTRIM('***SQL', '*'),
RTRIM('SQL***', '*'),
TRIM(' SQL ')
FROM dual;
TRIM은 문자열 중간에 있는 공백까지 제거하지 않는다.
SELECT LPAD('7', 3, '0'),
RPAD('SQL', 5, '*'),
'SQL' || 'D'
FROM dual;
007, SQL**, SQLD
LPAD : 왼쪽을 지정한 문자로 채운다.RPAD : 오른쪽을 지정한 문자로 채운다.|| : Oracle에서 문자열을 연결한다.| 함수 | 설명 | 예시 결과 |
|---|---|---|
ABS(-10) | 절댓값 | 10 |
SIGN(-10) | 양수 1, 0은 0, 음수 -1 | -1 |
MOD(10, 3) | 나머지 | 1 |
CEIL(3.1) | 입력값 이상의 가장 작은 정수 | 4 |
FLOOR(3.9) | 입력값 이하의 가장 큰 정수 | 3 |
ROUND(123.456, 2) | 반올림 | 123.46 |
TRUNC(123.456, 2) | 버림 | 123.45 |
ROUND와 TRUNC에서 자리수가 음수이면 소수점 왼쪽을 처리한다.
SELECT ROUND(1234.567, -2),
TRUNC(1234.567, -2)
FROM dual;
-- 결과: 1200, 1200
양수 자리수는 소수점 오른쪽, 음수 자리수는 소수점 왼쪽이다.
Oracle에서는 SYSDATE로 현재 날짜와 시간을 구한다.
SELECT SYSDATE
FROM dual;
Oracle의 날짜 연산에서 숫자 1은 하루를 의미한다.
SELECT SYSDATE + 1, -- 하루 뒤
SYSDATE - 7 -- 7일 전
FROM dual;
| 함수 | 설명 |
|---|---|
ADD_MONTHS(date, n) | 날짜에 n개월 추가 |
MONTHS_BETWEEN(d1, d2) | 두 날짜 사이의 개월 수 |
NEXT_DAY(date, 요일) | 이후 처음 오는 지정 요일 |
LAST_DAY(date) | 해당 월의 마지막 날짜 |
EXTRACT(요소 FROM date) | 연·월·일 등의 요소 추출 |
SELECT ADD_MONTHS(SYSDATE, 3),
LAST_DAY(SYSDATE),
EXTRACT(YEAR FROM SYSDATE)
FROM dual;
SELECT ROUND(SYSDATE, 'MONTH'),
TRUNC(SYSDATE, 'MONTH')
FROM dual;
TRUNC(date, 'MONTH') → 해당 월의 1일TRUNC(date, 'YEAR') → 해당 연도의 1월 1일ROUND(date, 'MONTH') → 16일 이상이면 다음 달 1일ROUND(date, 'YEAR') → 7월 1일 이상이면 다음 해 1월 1일| 함수 | 설명 |
|---|---|
TO_CHAR | 숫자 또는 날짜를 문자로 변환 |
TO_NUMBER | 문자를 숫자로 변환 |
TO_DATE | 문자를 날짜로 변환 |
CAST | 지정한 데이터형으로 변환 |
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS')
FROM dual;
SELECT TO_DATE('2026-08-23', 'YYYY-MM-DD')
FROM dual;
SELECT TO_NUMBER('1200') + 100
FROM dual;
자주 사용하는 날짜 형식은 다음과 같다.
| 형식 | 의미 |
|---|---|
YYYY | 네 자리 연도 |
MM | 월 |
DD | 일 |
HH24 | 24시간제 시 |
MI | 분 |
SS | 초 |
MM은 월이고MI는 분이다.
NULL은 숫자 0이나 공백이 아니라 알 수 없는 값을 의미한다.
salary + NULL -- 결과: NULL
NULL = NULL -- TRUE가 아니라 UNKNOWN
NULL 여부를 확인할 때는 =가 아니라 IS NULL을 사용한다.
SELECT *
FROM emp
WHERE comm IS NULL;
값이 NULL이면 다른 값으로 바꾼다.
NVL(값, NULL일 때 반환할 값)
SELECT ename,
sal + NVL(comm, 0) AS total_pay
FROM emp;
NVL2(값, NULL이 아닐 때, NULL일 때)
SELECT NVL2(comm, '수당 있음', '수당 없음')
FROM emp;
두 값이 같으면 NULL, 다르면 첫 번째 값을 반환한다.
SELECT NULLIF(10, 10),
NULLIF(10, 20)
FROM dual;
-- 결과: NULL, 10
왼쪽부터 확인하여 처음 만나는 NULL이 아닌 값을 반환한다.
SELECT COALESCE(NULL, NULL, 300, 400)
FROM dual;
-- 결과: 300
| 함수 | 핵심 의미 |
|---|---|
NVL(a, b) | a가 NULL이면 b |
NVL2(a, b, c) | a가 NULL이 아니면 b, NULL이면 c |
NULLIF(a, b) | a와 b가 같으면 NULL, 다르면 a |
COALESCE(a, b, ...) | 처음 만나는 NULL이 아닌 값 |
Oracle에서 사용하는 조건 함수로, 비교값이 같은지 차례대로 확인한다.
SELECT ename,
DECODE(deptno,
10, 'ACCOUNTING',
20, 'RESEARCH',
30, 'SALES',
'ETC') AS dept_name
FROM emp;
CASE는 대부분의 DBMS에서 사용할 수 있으며, 범위나 복합 조건도 표현할 수 있다.
SELECT ename,
CASE
WHEN sal >= 3000 THEN 'HIGH'
WHEN sal >= 2000 THEN 'MIDDLE'
ELSE 'LOW'
END AS salary_grade
FROM emp;
여러 조건이 참이어도 위에서부터 처음 만족한 결과를 반환한다.
ELSE를 생략하고 어떤 조건도 만족하지 않으면 NULL이 반환된다.
| 함수 | 설명 |
|---|---|
COUNT | 행의 개수 |
SUM | 합계 |
AVG | 평균 |
MAX | 최댓값 |
MIN | 최솟값 |
STDDEV | 표준편차 |
VARIANCE | 분산 |
SELECT deptno,
COUNT(*) AS emp_count,
SUM(sal) AS total_sal,
AVG(sal) AS avg_sal
FROM emp
GROUP BY deptno;
COUNT(*) -- 전체 행의 수
COUNT(comm) -- comm이 NULL이 아닌 행의 수
COUNT(DISTINCT deptno) -- 중복을 제거한 NULL이 아닌 값의 수
컬럼 값이 100, 200, NULL이라면 결과는 다음과 같다.
| 표현 | 결과 |
|---|---|
COUNT(*) | 3 |
COUNT(컬럼) | 2 |
SUM(컬럼) | 300 |
AVG(컬럼) | 150 |
대부분의 집계 함수는 NULL을 제외하고 계산한다. 따라서 다음 두 식의 결과는 다를 수 있다.
AVG(comm)
AVG(NVL(comm, 0))
SELECT deptno, AVG(sal)
FROM emp
WHERE sal >= 1000
GROUP BY deptno
HAVING AVG(sal) >= 2000;
WHERE → 그룹화하기 전에 개별 행을 필터링HAVING → 그룹화한 후 그룹을 필터링계층적인 소계와 전체 합계를 구한다.
SELECT deptno, job, SUM(sal)
FROM emp
GROUP BY ROLLUP(deptno, job);
ROLLUP(A, B)가 만드는 그룹은 다음과 같다.
(A, B) → (A) → ()
즉, 부서·직무별 합계 → 부서별 소계 → 전체 총계가 출력된다.
가능한 모든 조합의 소계와 전체 합계를 구한다.
CUBE(A, B)
→ (A, B), (A), (B), ()
필요한 그룹 조합만 직접 선택한다.
SELECT deptno, job, SUM(sal)
FROM emp
GROUP BY GROUPING SETS ((deptno), (job), ());
실제 데이터의 NULL과 소계·총계 때문에 생성된 NULL을 구분한다.
GROUPING(컬럼) = 0GROUPING(컬럼) = 1| 표현 | 생성되는 그룹 |
|---|---|
ROLLUP(A, B) | (A,B), (A), () |
CUBE(A, B) | (A,B), (A), (B), () |
GROUPING SETS((A),(B)) | (A), (B) |
윈도우 함수는 여러 행을 이용해 계산하지만, 집계 함수와 달리 기존 행을 유지한다.
함수() OVER (
PARTITION BY 그룹기준
ORDER BY 정렬기준
)
PARTITION BY → 계산할 그룹을 나눈다.ORDER BY → 그룹 안에서 계산 순서를 정한다.PARTITION BY를 생략하면 전체 행을 하나의 그룹으로 본다.점수가 100, 100, 90일 때 결과는 다음과 같다.
| 점수 | RANK | DENSE_RANK | ROW_NUMBER |
|---|---|---|---|
| 100 | 1 | 1 | 1 |
| 100 | 1 | 1 | 2 |
| 90 | 3 | 2 | 3 |
RANK → 공동 순위 다음 번호를 건너뛴다.DENSE_RANK → 공동 순위가 있어도 다음 번호가 이어진다.ROW_NUMBER → 동점이어도 서로 다른 번호를 부여한다.SELECT ename,
sal,
RANK() OVER (ORDER BY sal DESC) AS rank_value,
DENSE_RANK() OVER (ORDER BY sal DESC) AS dense_rank_value,
ROW_NUMBER() OVER (ORDER BY sal DESC) AS row_num
FROM emp;
SELECT empno,
sal,
LAG(sal) OVER (ORDER BY empno) AS prev_sal,
LEAD(sal) OVER (ORDER BY empno) AS next_sal
FROM emp;
LAG → 이전 행의 값LEAD → 다음 행의 값SELECT empno,
sal,
SUM(sal) OVER (
ORDER BY empno
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumulative_sal
FROM emp;
UNBOUNDED PRECEDING → 첫 번째 행부터CURRENT ROW → 현재 행까지| 기능 | Oracle | SQL Server |
|---|---|---|
| 현재 날짜 | SYSDATE | GETDATE() |
| NULL 대체 | NVL(a, b) | ISNULL(a, b) |
| 문자열 길이 | LENGTH(a) | LEN(a) |
| 문자열 추출 | SUBSTR(a, 시작, 길이) | SUBSTRING(a, 시작, 길이) |
| 문자 연결 | a \|\| b | a + b 또는 CONCAT(a,b) |
| 나머지 | MOD(a, b) | a % b |
| 날짜 더하기 | 날짜에 숫자 더하기, ADD_MONTHS | DATEADD |
| 날짜 차이 | 날짜끼리 빼기, MONTHS_BETWEEN | DATEDIFF |
| 날짜 요소 추출 | EXTRACT | DATEPART |
| 형 변환 | TO_CHAR, TO_DATE, CAST | CAST, CONVERT |
| 조건 처리 | DECODE, CASE | CASE |
COALESCE, CASE, CAST는 두 DBMS에서 모두 사용할 수 있다.
아래 내용은 시험 직전에 꼭 다시 확인해야 한다.
NULL = NULL은 참이 아니다. NULL 비교는 IS NULL을 사용한다.COUNT(*)는 전체 행, COUNT(컬럼)은 NULL이 아닌 값만 센다.AVG(컬럼)은 NULL을 제외한다.WHERE는 그룹화 전, HAVING은 그룹화 후 조건이다.RANK는 순위를 건너뛰고 DENSE_RANK는 건너뛰지 않는다.ROW_NUMBER는 동점이어도 서로 다른 번호를 부여한다.ROLLUP은 계층적 조합, CUBE는 가능한 모든 조합이다.GROUPING은 소계 때문에 생성된 NULL이면 1을 반환한다.MM은 월, MI는 분이다.DESC는 내림차순 정렬 키워드이지 함수가 아니다.IS NULL도 함수가 아니라 NULL을 판단하는 조건이다.문자 추출 SUBSTR
문자 위치 INSTR
문자 길이 LENGTH / SQL Server는 LEN
반올림 ROUND
버림 TRUNC
NULL 대체 NVL / SQL Server는 ISNULL
첫 비NULL 값 COALESCE
두 값이 같으면 NULL NULLIF
조건 분기 CASE / Oracle은 DECODE 가능
전체 행 수 COUNT(*)
비NULL 값 수 COUNT(컬럼)
계층별 소계 ROLLUP
모든 조합 소계 CUBE
선택한 조합 집계 GROUPING SETS
공동 순위 건너뜀 RANK
공동 순위 연속 DENSE_RANK
고유 순번 ROW_NUMBER
이전 행 LAG
다음 행 LEAD
SQLD 함수 문제는 모든 함수의 문법을 통째로 외우는 것보다 다음 네 가지를 확실히 구분하는 것이 중요하다.
시험 전에는 새로운 내용을 더 외우기보다 위의 시험 직전 체크 포인트와 마지막 1분 암기표를 반복해서 확인해야겠다.