Section 01. 단일행 함수
단일행 함수는 행 하나에 적용되어 결과를 하나씩 반환하는 함수다. GROUP BY와 함께 쓸 수 있고, WHERE 절에도 사용할 수 있다.
문자 함수
| 함수 | 설명 | 예시 |
|---|---|---|
UPPER(str) |
대문자로 변환 | UPPER('hello') → HELLO |
LOWER(str) |
소문자로 변환 | LOWER('HELLO') → hello |
LENGTH(str) |
문자열 길이 | LENGTH('hello') → 5 |
SUBSTR(str, pos, len) |
부분 문자열 추출 | SUBSTR('HELLO', 2, 3) → ELL |
INSTR(str, sub) |
부분 문자열 위치 | INSTR('HELLO', 'L') → 3 |
LPAD(str, n, pad) |
왼쪽 채우기 | LPAD('7', 3, '0') → 007 |
RPAD(str, n, pad) |
오른쪽 채우기 | RPAD('7', 3, '0') → 700 |
LTRIM(str) |
왼쪽 공백 제거 | LTRIM(' hi') → hi |
RTRIM(str) |
오른쪽 공백 제거 | RTRIM('hi ') → hi |
TRIM(str) |
양쪽 공백 제거 | TRIM(' hi ') → hi |
REPLACE(str, old, new) |
문자열 치환 | REPLACE('HELLO', 'L', 'R') → HERRO |
CONCAT(str1, str2) |
문자열 연결 | CONCAT('HEL', 'LO') → HELLO |
SUBSTR의 위치는 1부터 시작한다. SUBSTR('HELLO', 2, 3)은 2번째 문자부터 3개를 추출해서 ELL이 된다.
숫자 함수
| 함수 | 설명 | 예시 |
|---|---|---|
ROUND(n, pos) |
반올림 | ROUND(3.567, 2) → 3.57 |
TRUNC(n, pos) |
버림 | TRUNC(3.567, 2) → 3.56 |
CEIL(n) |
올림 | CEIL(3.2) → 4 |
FLOOR(n) |
내림 | FLOOR(3.9) → 3 |
MOD(n, m) |
나머지 | MOD(10, 3) → 1 |
ABS(n) |
절댓값 | ABS(-5) → 5 |
SIGN(n) |
부호 | SIGN(-3) → -1 |
POWER(n, m) |
거듭제곱 | POWER(2, 3) → 8 |
ROUND와 TRUNC에서 pos가 음수면 소수점 왼쪽을 기준으로 동작한다. ROUND(125, -1) → 130.
날짜 함수
| 함수 | 설명 |
|---|---|
SYSDATE |
현재 날짜와 시간 |
SYSTIMESTAMP |
현재 타임스탬프 |
ADD_MONTHS(date, n) |
n개월 더하기 |
MONTHS_BETWEEN(d1, d2) |
두 날짜 사이의 개월 수 |
NEXT_DAY(date, day) |
다음 특정 요일 날짜 |
LAST_DAY(date) |
해당 월의 마지막 날 |
ROUND(date, fmt) |
날짜 반올림 |
TRUNC(date, fmt) |
날짜 버림 |
SELECT SYSDATE FROM DUAL;
SELECT ADD_MONTHS(SYSDATE, 3) FROM DUAL; -- 3개월 후
SELECT MONTHS_BETWEEN(SYSDATE, '2024-01-01') FROM DUAL;
SELECT LAST_DAY(SYSDATE) FROM DUAL; -- 이번 달 마지막 날
날짜끼리 빼면 일수가 나온다. SYSDATE - 7은 7일 전 날짜다.
변환 함수
| 함수 | 설명 |
|---|---|
TO_CHAR(val, fmt) |
숫자/날짜 → 문자 |
TO_NUMBER(str, fmt) |
문자 → 숫자 |
TO_DATE(str, fmt) |
문자 → 날짜 |
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD') FROM DUAL; -- '2026-03-14'
SELECT TO_CHAR(12345.6, '99,999.9') FROM DUAL; -- '12,345.6'
SELECT TO_DATE('2026-03-14', 'YYYY-MM-DD') FROM DUAL;
SELECT TO_NUMBER('12345') FROM DUAL;
NULL 관련 함수
| 함수 | 설명 |
|---|---|
NVL(expr, val) |
NULL이면 val 반환 |
NVL2(expr, val1, val2) |
NULL이 아니면 val1, NULL이면 val2 |
NULLIF(expr1, expr2) |
두 값이 같으면 NULL, 다르면 expr1 |
COALESCE(e1, e2, ...) |
첫 번째 NULL이 아닌 값 반환 |
SELECT NVL(COMMISSION, 0) FROM EMP; -- 커미션 없으면 0
SELECT NVL2(COMMISSION, '있음', '없음') FROM EMP;
SELECT NULLIF(10, 10) FROM DUAL; -- NULL
SELECT NULLIF(10, 20) FROM DUAL; -- 10
SELECT COALESCE(NULL, NULL, 'hello') FROM DUAL; -- 'hello'
NVL은 가장 자주 나온다. NULL을 그냥 두면 집계 함수에서 제외되거나 연산 결과가 NULL이 되어버리기 때문에 처리해 두는 것이 좋다.
CASE 표현식
조건에 따라 다른 값을 반환한다. 두 가지 형태가 있다.
단순 CASE
SELECT NAME,
CASE STATUS
WHEN 'ACTIVE' THEN '활성'
WHEN 'INACTIVE' THEN '비활성'
ELSE '기타'
END AS STATUS_KO
FROM MEMBER;
검색 CASE
SELECT NAME,
CASE
WHEN AGE < 20 THEN '미성년'
WHEN AGE < 65 THEN '성인'
ELSE '노인'
END AS AGE_GROUP
FROM MEMBER;
DECODE는 Oracle 전용 함수로 단순 CASE와 비슷하게 동작한다.
SELECT DECODE(STATUS, 'ACTIVE', '활성', 'INACTIVE', '비활성', '기타') FROM MEMBER;
Section 02. GROUP BY / HAVING
집계 함수
여러 행을 하나로 집계하는 함수. NULL은 집계에서 제외된다.
| 함수 | 설명 |
|---|---|
COUNT(*) |
전체 행 수 (NULL 포함) |
COUNT(col) |
NULL 제외 행 수 |
SUM(col) |
합계 |
AVG(col) |
평균 (NULL 제외) |
MAX(col) |
최댓값 |
MIN(col) |
최솟값 |
STDDEV(col) |
표준편차 |
VARIANCE(col) |
분산 |
SELECT COUNT(*), COUNT(EMAIL), SUM(AGE), AVG(AGE), MAX(AGE), MIN(AGE)
FROM MEMBER;
COUNT(*)와 COUNT(컬럼명)의 차이가 자주 나온다. COUNT(*)는 NULL이 있는 행도 세고, COUNT(EMAIL)은 EMAIL이 NULL인 행을 제외하고 센다.
GROUP BY
행들을 특정 컬럼 기준으로 묶어 집계한다.
SELECT STATUS, COUNT(*), AVG(AGE)
FROM MEMBER
GROUP BY STATUS;
GROUP BY에 명시되지 않은 컬럼은 SELECT에 단독으로 쓸 수 없다. 집계 함수로 감싸야 한다.
-- 오류: NAME은 GROUP BY에 없고 집계 함수도 없음
SELECT STATUS, NAME, COUNT(*) FROM MEMBER GROUP BY STATUS;
-- 정상
SELECT STATUS, COUNT(*) FROM MEMBER GROUP BY STATUS;
여러 컬럼으로 묶을 수도 있다.
SELECT STATUS, SUBSTR(NAME, 1, 1) AS 성씨, COUNT(*)
FROM MEMBER
GROUP BY STATUS, SUBSTR(NAME, 1, 1);
HAVING
GROUP BY 결과에 조건을 걸 때 사용한다. WHERE는 그룹화 이전에, HAVING은 그룹화 이후에 적용된다.
SELECT STATUS, COUNT(*)
FROM MEMBER
GROUP BY STATUS
HAVING COUNT(*) >= 10;
집계 함수 조건은 WHERE 절에 쓸 수 없다. 반드시 HAVING을 써야 한다.
-- 오류: 집계 함수를 WHERE에 사용 불가
SELECT STATUS, COUNT(*) FROM MEMBER WHERE COUNT(*) >= 10 GROUP BY STATUS;
-- 정상
SELECT STATUS, COUNT(*) FROM MEMBER GROUP BY STATUS HAVING COUNT(*) >= 10;
SELECT 실행 순서
SQL이 실제로 처리되는 순서다. 시험에서 종종 물어보고, 어떤 절에서 어떤 별칭을 쓸 수 있는지 판단하는 기준이 된다.
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
- WHERE에서는 SELECT의 별칭을 쓸 수 없다 (SELECT보다 먼저 실행)
- ORDER BY에서는 SELECT의 별칭을 쓸 수 있다 (SELECT보다 나중에 실행)
- HAVING에서는 GROUP BY 이후 집계된 결과에 조건을 건다
Section 03. ORDER BY
결과를 특정 기준으로 정렬한다. SELECT 문에서 가장 마지막에 실행된다.
SELECT NAME, AGE
FROM MEMBER
ORDER BY AGE ASC; -- 오름차순 (기본값)
SELECT NAME, AGE
FROM MEMBER
ORDER BY AGE DESC; -- 내림차순
ASC는 생략 가능하다. 생략하면 오름차순이다.
여러 컬럼으로 정렬할 수 있다. 앞에 있는 기준이 우선이고, 같은 값이 있을 때 다음 기준을 적용한다.
SELECT NAME, AGE, STATUS
FROM MEMBER
ORDER BY STATUS ASC, AGE DESC;
ORDER BY에서는 SELECT 별칭이나 컬럼 번호를 쓸 수 있다.
SELECT NAME, AGE AS MEMBER_AGE
FROM MEMBER
ORDER BY MEMBER_AGE DESC; -- 별칭 사용
SELECT NAME, AGE
FROM MEMBER
ORDER BY 2 DESC; -- 2번째 컬럼(AGE) 기준
컬럼 번호 방식은 SELECT 컬럼 순서가 바뀌면 의도치 않은 정렬이 생길 수 있어서 실무에서는 잘 안 쓴다.
NULL 정렬
Oracle에서 NULL은 기본적으로 가장 큰 값으로 취급된다.
ASC정렬: NULL이 마지막DESC정렬: NULL이 처음
NULLS FIRST, NULLS LAST로 명시적으로 지정할 수 있다.
SELECT NAME, EMAIL
FROM MEMBER
ORDER BY EMAIL ASC NULLS FIRST; -- NULL을 먼저
Section 04. JOIN
두 개 이상의 테이블을 연결해서 데이터를 조회한다.
INNER JOIN (내부 조인)
두 테이블에서 조건을 만족하는 행만 반환
조건에 맞는 행이 있을 때만 결과에 포함된다. 한쪽에만 있는 행은 제외된다.
SELECT M.NAME, O.ORDER_DATE
FROM MEMBER M
INNER JOIN ORDERS O ON M.MEMBER_ID = O.MEMBER_ID;
Oracle 구식 문법:
SELECT M.NAME, O.ORDER_DATE
FROM MEMBER M, ORDERS O
WHERE M.MEMBER_ID = O.MEMBER_ID;
LEFT OUTER JOIN (왼쪽 외부 조인)
왼쪽 테이블의 모든 행 + 오른쪽 테이블에서 조건을 만족하는 행
오른쪽에 매칭되는 행이 없으면 NULL로 채워진다.
SELECT M.NAME, O.ORDER_DATE
FROM MEMBER M
LEFT OUTER JOIN ORDERS O ON M.MEMBER_ID = O.MEMBER_ID;
주문이 없는 회원도 결과에 포함되고, 해당 주문 컬럼은 NULL이 된다.
Oracle 구식 문법에서는 오른쪽 테이블 조건 뒤에 (+)를 붙인다.
SELECT M.NAME, O.ORDER_DATE
FROM MEMBER M, ORDERS O
WHERE M.MEMBER_ID = O.MEMBER_ID(+);
RIGHT OUTER JOIN (오른쪽 외부 조인)
오른쪽 테이블의 모든 행 + 왼쪽 테이블에서 조건을 만족하는 행
SELECT M.NAME, O.ORDER_DATE
FROM MEMBER M
RIGHT OUTER JOIN ORDERS O ON M.MEMBER_ID = O.MEMBER_ID;
Oracle 구식:
WHERE M.MEMBER_ID(+) = O.MEMBER_ID
FULL OUTER JOIN (전체 외부 조인)
양쪽 테이블의 모든 행. 조건을 만족하지 않는 쪽은 NULL로 채워짐
SELECT M.NAME, O.ORDER_DATE
FROM MEMBER M
FULL OUTER JOIN ORDERS O ON M.MEMBER_ID = O.MEMBER_ID;
Oracle 구식 문법으로는 FULL OUTER JOIN을 표현할 수 없다.
CROSS JOIN (교차 조인)
두 테이블의 모든 행 조합. 카르테시안 곱
SELECT M.NAME, P.PRODUCT_NAME
FROM MEMBER M
CROSS JOIN PRODUCT P;
조건 없이 FROM에 두 테이블을 나열하면 자동으로 CROSS JOIN이 된다. MEMBER가 100행, PRODUCT가 50행이면 5000행이 나온다.
NATURAL JOIN
두 테이블에서 이름이 같은 컬럼을 기준으로 자동 조인한다. 명시적이지 않아서 실무에서는 잘 안 쓴다.
SELECT * FROM MEMBER NATURAL JOIN ORDERS;
SELF JOIN
같은 테이블을 두 번 조인한다. 직원과 매니저 관계처럼 같은 테이블 내의 행끼리 연결할 때 쓴다.
SELECT E.NAME AS 직원, M.NAME AS 매니저
FROM EMP E
LEFT JOIN EMP M ON E.MANAGER_ID = M.EMP_ID;
JOIN 비교 요약
| 종류 | 설명 | 매칭 안 되는 행 |
|---|---|---|
| INNER JOIN | 양쪽 모두 조건 만족 | 제외 |
| LEFT OUTER JOIN | 왼쪽 모두 포함 | 오른쪽 NULL |
| RIGHT OUTER JOIN | 오른쪽 모두 포함 | 왼쪽 NULL |
| FULL OUTER JOIN | 양쪽 모두 포함 | 상대쪽 NULL |
| CROSS JOIN | 모든 조합 | - |
'Study > SQL' 카테고리의 다른 글
| [SQLD] Part 02 - SQL 기본 (1) (0) | 2026.03.14 |
|---|---|
| [SQLD] Part 01 - 데이터 모델링 (1) | 2026.03.03 |
