[SQLD] Part 03 - SQL 기본 (2)


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

ROUNDTRUNC에서 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