콘텐츠로 이동

데이터 가공

데이터 가공: SQL 쿼리 내에서 값을 변환·조합·조건 분기하는 기능. 애플리케이션 코드로 처리하지 않고 DB 레벨에서 원하는 형태로 가공해 반환한다.

왜 쓰는가?

  • 애플리케이션에서 가공 로직을 줄여 코드 단순화
  • DB에서 한 번에 처리하므로 네트워크 전송 데이터 감소
  • NULL 처리, 조건 분기 등 쿼리 결과를 즉시 사용 가능한 형태로 반환

조건 분기 — CASE WHEN

값에 따라 다른 결과를 반환하는 SQL의 if-else.

단순 CASE (값 비교)

SELECT
    name,
    CASE grade
        WHEN 'A' THEN '우수'
        WHEN 'B' THEN '보통'
        WHEN 'C' THEN '미흡'
        ELSE '미분류'
    END AS 등급명
FROM student;

검색 CASE (범위·복합 조건)

SELECT
    name,
    salary,
    CASE
        WHEN salary >= 5000000 THEN '고연봉'
        WHEN salary >= 3000000 THEN '중연봉'
        ELSE '저연봉'
    END AS 연봉구간
FROM employee;
-- 집계와 함께: 상태별 건수 피벗
SELECT
    COUNT(CASE WHEN status = 'PAID'    THEN 1 END) AS 결제완료,
    COUNT(CASE WHEN status = 'PENDING' THEN 1 END) AS 결제대기,
    COUNT(CASE WHEN status = 'CANCEL'  THEN 1 END) AS 취소
FROM orders;

NULL 처리 함수

함수 설명 예시
COALESCE(a, b, c, ...) 첫 번째 non-NULL 값 반환 COALESCE(nickname, name, '익명')
IFNULL(a, b) a가 NULL이면 b 반환 (MySQL) IFNULL(score, 0)
NULLIF(a, b) a = b이면 NULL 반환, 아니면 a NULLIF(quantity, 0)
IS NULL / IS NOT NULL NULL 여부 조건 WHERE email IS NOT NULL
-- 닉네임 > 이름 > '익명' 순으로 표시
SELECT COALESCE(nickname, name, '익명') AS 표시명 FROM member;

-- 0으로 나누기 방지 (NULLIF로 분모가 0이면 NULL → 결과도 NULL)
SELECT total_sales / NULLIF(order_count, 0) AS 건당_매출 FROM stats;

주의: NULL = NULLFALSE다. NULL 비교는 반드시 IS NULL / IS NOT NULL 사용. WHERE column = NULL은 항상 결과가 없다.


문자열 함수

함수 설명 예시
CONCAT(a, b, ...) 문자열 연결 CONCAT(first_name, ' ', last_name)
SUBSTRING(str, pos, len) 부분 문자열 추출 SUBSTRING('hello', 2, 3)ell
LENGTH(str) 바이트 길이 LENGTH('abc')3
CHAR_LENGTH(str) 문자 수 (한글 정확) CHAR_LENGTH('한글')2
UPPER(str) / LOWER(str) 대/소문자 변환 UPPER('hello')HELLO
TRIM(str) 앞뒤 공백 제거 TRIM(' hi ')hi
REPLACE(str, from, to) 문자열 치환 REPLACE('abc', 'b', 'X')aXc
LIKE 패턴 매칭 name LIKE '김%'
-- 이름 마스킹 (첫 글자만 표시)
SELECT CONCAT(SUBSTRING(name, 1, 1), '**') AS 마스킹명 FROM member;

숫자 함수

함수 설명 예시
ROUND(n, d) 반올림 (d: 소수점 자릿수) ROUND(3.567, 2)3.57
FLOOR(n) 내림 (정수) FLOOR(3.9)3
CEIL(n) 올림 (정수) CEIL(3.1)4
ABS(n) 절댓값 ABS(-5)5
MOD(n, m) 나머지 MOD(10, 3)1
-- 10% 할인 후 백 원 단위 반올림
SELECT ROUND(price * 0.9, -2) AS 할인가 FROM product;

날짜 함수

함수 설명 예시
NOW() 현재 날짜시간 2024-01-15 14:30:00
CURDATE() 현재 날짜 2024-01-15
DATE_FORMAT(d, fmt) 날짜 포맷 변환 DATE_FORMAT(NOW(), '%Y-%m')
DATEDIFF(d1, d2) 날짜 차이 (일 수) DATEDIFF('2024-01-15', '2024-01-01')14
DATE_ADD(d, INTERVAL n unit) 날짜 더하기 DATE_ADD(NOW(), INTERVAL 7 DAY)
YEAR(d) / MONTH(d) / DAY(d) 연·월·일 추출 YEAR('2024-01-15')2024
-- 최근 30일 가입 회원
SELECT * FROM member
WHERE created_at >= DATE_ADD(NOW(), INTERVAL -30 DAY);

-- 월별 매출 집계
SELECT
    DATE_FORMAT(order_date, '%Y-%m') AS ,
    SUM(amount) AS 월매출
FROM orders
GROUP BY DATE_FORMAT(order_date, '%Y-%m')
ORDER BY ;

형변환 — CAST / CONVERT

-- 문자열 → 숫자
SELECT CAST('123' AS UNSIGNED);
SELECT CAST('3.14' AS DECIMAL(10, 2));

-- 숫자 → 문자열
SELECT CAST(age AS CHAR) FROM member;

-- 날짜 변환
SELECT CAST('2024-01-15' AS DATE);
SELECT CONVERT('2024-01-15', DATE); -- CONVERT는 MySQL 방언

언제 쓰는지

상황 함수/기능 사용 시점
조건에 따라 값 변환 ✅ CASE WHEN 상태 코드 → 텍스트, 범위별 분류
NULL 처리 ✅ COALESCE / IFNULL 기본값 대체, 필수 정보 표시
문자 추출/합치기 ✅ SUBSTRING, CONCAT 마스킹, 이름 조합
날짜 비교 ✅ DATE_FORMAT, DATEDIFF 월별·연도별 필터링
숫자 계산 ✅ ROUND, MOD 할인가, 나머지 계산
타입 변환 ✅ CAST 문자→숫자, 숫자→문자
기본값 설정 ✅ NULLIF 0 제외, 분모 처리

특징

함수별 특징

1. 함수별 NULL 처리

-- 대부분의 함수는 NULL 입력 → NULL 출력
CONCAT('a', NULL, 'b')     -- NULL (NOT 'ab')
SUBSTRING(NULL, 1, 2)      -- NULL
DATE_FORMAT(NULL, '%Y-%m') -- NULL

-- 예외: NULL 관련 함수
COALESCE(NULL, 'default')  -- 'default' (NULL 입력도 처리)
IFNULL(NULL, 0)            -- 0

2. 인덱스와 함수 (성능 영향)

-- ❌ 함수를 쓰면 인덱스 무효화
WHERE YEAR(created_at) = 2024        -- 풀 스캔
WHERE LENGTH(name) = 5               -- 풀 스캔

-- ✅ 인덱스 활용
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01'
WHERE name LIKE 'Kim%'  -- LIKE는 인덱스 활용

3. CASE의 실행 순서

-- WHEN을 위에서부터 순서대로 평가, 첫 TRUE에서 멈춤
CASE 
    WHEN age < 20 THEN '청소년'     -- 1순위 체크
    WHEN age < 65 THEN '성인'       -- 위가 FALSE일 때만
    ELSE '노년'
END;
-- age = 25인 경우: "성인" (첫 번째 FALSE, 두 번째 TRUE)

4. GROUP BY와 함께 쓸 때

-- CASE와 COUNT 조합으로 조건별 집계
SELECT
    COUNT(CASE WHEN status = 'ACTIVE' THEN 1 END)   AS 활성,
    COUNT(CASE WHEN status = 'INACTIVE' THEN 1 END) AS 비활성
FROM member;

장점

  • DB 레벨 가공으로 애플리케이션 코드 단순화
  • 쿼리 한 번으로 원하는 형태의 데이터 반환

단점

  • 복잡한 가공 로직은 쿼리 가독성 저하
  • 함수 적용 컬럼은 인덱스를 타지 못한다

주의할 점

인덱스 무효화: WHERE YEAR(created_at) = 2024처럼 인덱스 컬럼에 함수를 씌우면 인덱스 풀 스캔이 발생한다.

-- 나쁜 예: 인덱스 무효화
WHERE YEAR(created_at) = 2024

-- 좋은 예: 범위 조건으로 인덱스 활용
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01'

NULL 전파: 대부분의 함수는 인자 중 하나라도 NULL이면 결과가 NULL이 된다. CONCAT('a', NULL, 'b')NULL. NULL이 섞일 수 있는 경우 COALESCE로 기본값을 지정한다.