데이터베이스에서 날짜와 시간 처리는 매우 중요한 기능 중 하나입니다. 하지만 Oracle, MySQL, MS SQL Server는 각각 다른 날짜 함수와 문법을 제공하여, 개발자들이 다른 데이터베이스로 마이그레이션할 때 가장 혼란스러워하는 부분 중 하나입니다. 각 데이터베이스 시스템의 날짜 함수 차이점과 대체 방법을 상세히 비교해보겠습니다.
Oracle
MySQL
MS SQL Server
🕐 1. 현재 날짜/시간 구하기
가장 기본적인 현재 날짜와 시간을 구하는 함수부터 각 데이터베이스마다 다릅니다.
기능 | Oracle | MySQL | MS SQL Server |
---|---|---|---|
현재 날짜+시간 | SYSDATE SYSTIMESTAMP |
NOW() CURRENT_TIMESTAMP |
GETDATE() CURRENT_TIMESTAMP |
현재 날짜만 | TRUNC(SYSDATE) | CURDATE() CURRENT_DATE |
CAST(GETDATE() AS DATE) |
현재 시간만 | TO_CHAR(SYSDATE, 'HH24:MI:SS') | CURTIME() CURRENT_TIME |
CAST(GETDATE() AS TIME) |
Oracle 예제
-- 현재 날짜/시간
SELECT SYSDATE FROM dual;
-- 결과: 2025-01-15 14:30:25
-- 타임스탬프 (밀리초 포함)
SELECT SYSTIMESTAMP FROM dual;
-- 결과: 2025-01-15 14:30:25.123456 +09:00
-- 현재 날짜만
SELECT TRUNC(SYSDATE) FROM dual;
-- 결과: 2025-01-15 00:00:00
MySQL 예제
-- 현재 날짜/시간
SELECT NOW();
-- 결과: 2025-01-15 14:30:25
-- 현재 날짜만
SELECT CURDATE();
-- 결과: 2025-01-15
-- 현재 시간만
SELECT CURTIME();
-- 결과: 14:30:25
MS SQL Server 예제
-- 현재 날짜/시간
SELECT GETDATE();
-- 결과: 2025-01-15 14:30:25.123
-- 현재 날짜만
SELECT CAST(GETDATE() AS DATE);
-- 결과: 2025-01-15
-- 현재 시간만
SELECT CAST(GETDATE() AS TIME);
-- 결과: 14:30:25.1230000
➕ 2. 날짜 연산 (더하기/빼기)
날짜에 일, 월, 년을 더하거나 빼는 방법도 각 데이터베이스마다 완전히 다릅니다.
연산 | Oracle | MySQL | MS SQL Server |
---|---|---|---|
일 더하기 | SYSDATE + 7 | DATE_ADD(NOW(), INTERVAL 7 DAY) | DATEADD(day, 7, GETDATE()) |
월 더하기 | ADD_MONTHS(SYSDATE, 3) | DATE_ADD(NOW(), INTERVAL 3 MONTH) | DATEADD(month, 3, GETDATE()) |
년 더하기 | ADD_MONTHS(SYSDATE, 12) | DATE_ADD(NOW(), INTERVAL 1 YEAR) | DATEADD(year, 1, GETDATE()) |
일 빼기 | SYSDATE - 30 | DATE_SUB(NOW(), INTERVAL 30 DAY) | DATEADD(day, -30, GETDATE()) |
📝 실무 예제: 최근 30일간 주문 데이터 조회
Oracle
SELECT order_id, customer_id, order_date, total_amount
FROM orders
WHERE order_date >= SYSDATE - 30
AND order_date < SYSDATE + 1
ORDER BY order_date DESC;
-- 월별 조회 (지난 3개월)
SELECT order_id, customer_id, order_date
FROM orders
WHERE order_date >= ADD_MONTHS(SYSDATE, -3)
ORDER BY order_date DESC;
MySQL
SELECT order_id, customer_id, order_date, total_amount
FROM orders
WHERE order_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
AND order_date <= NOW()
ORDER BY order_date DESC;
-- 월별 조회 (지난 3개월)
SELECT order_id, customer_id, order_date
FROM orders
WHERE order_date >= DATE_SUB(NOW(), INTERVAL 3 MONTH)
ORDER BY order_date DESC;
MS SQL Server
SELECT order_id, customer_id, order_date, total_amount
FROM orders
WHERE order_date >= DATEADD(day, -30, GETDATE())
AND order_date <= GETDATE()
ORDER BY order_date DESC;
-- 월별 조회 (지난 3개월)
SELECT order_id, customer_id, order_date
FROM orders
WHERE order_date >= DATEADD(month, -3, GETDATE())
ORDER BY order_date DESC;
📊 3. 날짜 차이 계산
두 날짜 간의 차이를 계산하는 방법도 각각 다릅니다.
차이 단위 | Oracle | MySQL | MS SQL Server |
---|---|---|---|
일 차이 | date2 - date1 | DATEDIFF(date2, date1) | DATEDIFF(day, date1, date2) |
월 차이 | MONTHS_BETWEEN(date2, date1) | TIMESTAMPDIFF(MONTH, date1, date2) | DATEDIFF(month, date1, date2) |
년 차이 | MONTHS_BETWEEN(date2, date1) / 12 | TIMESTAMPDIFF(YEAR, date1, date2) | DATEDIFF(year, date1, date2) |
MySQL의 DATEDIFF는 일 차이만 계산하고, 시간/분/초 차이는 TIMESTAMPDIFF를 사용해야 합니다. 반면 MS SQL Server의 DATEDIFF는 다양한 단위를 지원합니다.
🎨 4. 날짜 포맷팅
날짜를 원하는 형식의 문자열로 변환하는 방법도 각각 완전히 다릅니다.
형식 | Oracle | MySQL | MS SQL Server |
---|---|---|---|
YYYY-MM-DD | TO_CHAR(date, 'YYYY-MM-DD') | DATE_FORMAT(date, '%Y-%m-%d') | FORMAT(date, 'yyyy-MM-dd') |
DD/MM/YYYY | TO_CHAR(date, 'DD/MM/YYYY') | DATE_FORMAT(date, '%d/%m/%Y') | FORMAT(date, 'dd/MM/yyyy') |
한국어 형식 | TO_CHAR(date, 'YYYY"년" MM"월" DD"일"') | DATE_FORMAT(date, '%Y년 %m월 %d일') | FORMAT(date, 'yyyy년 MM월 dd일') |
Oracle 포맷팅
-- 다양한 형식 예제
SELECT
TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') AS datetime_format,
TO_CHAR(SYSDATE, 'YYYY"년" MM"월" DD"일"') AS korean_format,
TO_CHAR(SYSDATE, 'Day, DD Month YYYY') AS english_format,
TO_CHAR(SYSDATE, 'Q') AS quarter,
TO_CHAR(SYSDATE, 'WW') AS week_of_year
FROM dual;
MySQL 포맷팅
-- 다양한 형식 예제
SELECT
DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s') AS datetime_format,
DATE_FORMAT(NOW(), '%Y년 %m월 %d일') AS korean_format,
DATE_FORMAT(NOW(), '%W, %d %M %Y') AS english_format,
QUARTER(NOW()) AS quarter,
WEEK(NOW()) AS week_of_year;
MS SQL Server 포맷팅
-- 다양한 형식 예제
SELECT
FORMAT(GETDATE(), 'yyyy-MM-dd HH:mm:ss') AS datetime_format,
FORMAT(GETDATE(), 'yyyy년 MM월 dd일') AS korean_format,
FORMAT(GETDATE(), 'dddd, dd MMMM yyyy', 'en-US') AS english_format,
DATEPART(QUARTER, GETDATE()) AS quarter,
DATEPART(WEEK, GETDATE()) AS week_of_year;
🔧 5. 날짜 추출 함수
날짜에서 년, 월, 일 등의 특정 부분을 추출하는 방법입니다.
추출 요소 | Oracle | MySQL | MS SQL Server |
---|---|---|---|
년도 | EXTRACT(YEAR FROM date) | YEAR(date) | YEAR(date) |
월 | EXTRACT(MONTH FROM date) | MONTH(date) | MONTH(date) |
일 | EXTRACT(DAY FROM date) | DAY(date) | DAY(date) |
요일 | TO_CHAR(date, 'D') | DAYOFWEEK(date) | DATEPART(WEEKDAY, date) |
분기 | TO_CHAR(date, 'Q') | QUARTER(date) | DATEPART(QUARTER, date) |
요일 번호는 각 데이터베이스마다 다릅니다! Oracle과 MS SQL Server는 일요일이 1, MySQL은 일요일이 1이지만 설정에 따라 달라질 수 있습니다.
📅 6. 특별한 날짜 함수들
각 데이터베이스만의 독특한 날짜 관련 함수들을 살펴보겠습니다.
Oracle 특별 함수
-- LAST_DAY: 해당 월의 마지막 날
SELECT LAST_DAY(SYSDATE) FROM dual;
-- 결과: 2025-01-31
-- NEXT_DAY: 다음 요일 찾기
SELECT NEXT_DAY(SYSDATE, '일요일') FROM dual;
-- TRUNC: 날짜 자르기
SELECT TRUNC(SYSDATE, 'MONTH') FROM dual; -- 월 첫날
SELECT TRUNC(SYSDATE, 'YEAR') FROM dual; -- 년 첫날
-- ROUND: 날짜 반올림
SELECT ROUND(SYSDATE, 'MONTH') FROM dual;
-- ADD_MONTHS: 월 더하기 (말일 처리 자동)
SELECT ADD_MONTHS('2025-01-31', 1) FROM dual;
-- 결과: 2025-02-28 (2월 말일로 자동 조정)
MySQL 특별 함수
-- LAST_DAY: 해당 월의 마지막 날
SELECT LAST_DAY(NOW());
-- 결과: 2025-01-31
-- DAYNAME: 요일명 반환
SELECT DAYNAME(NOW());
-- 결과: Wednesday
-- WEEKDAY: 요일 번호 (월요일=0)
SELECT WEEKDAY(NOW());
-- STR_TO_DATE: 문자열을 날짜로 변환
SELECT STR_TO_DATE('2025-01-15', '%Y-%m-%d');
-- MAKEDATE: 년도와 일수로 날짜 생성
SELECT MAKEDATE(2025, 100); -- 2025년의 100번째 날
-- PERIOD_DIFF: 기간 차이 (YYYYMM 형식)
SELECT PERIOD_DIFF(202501, 202412);
-- 결과: 1 (1개월 차이)
MS SQL Server 특별 함수
-- EOMONTH: 해당 월의 마지막 날
SELECT EOMONTH(GETDATE());
-- 결과: 2025-01-31
-- DATEFROMPARTS: 년,월,일로 날짜 생성
SELECT DATEFROMPARTS(2025, 12, 25);
-- 결과: 2025-12-25
-- TIMEFROMPARTS: 시,분,초로 시간 생성
SELECT TIMEFROMPARTS(14, 30, 0, 0, 0);
-- ISDATE: 유효한 날짜인지 확인
SELECT ISDATE('2025-02-29'); -- 결과: 0 (무효)
SELECT ISDATE('2024-02-29'); -- 결과: 1 (유효, 윤년)
-- SWITCHOFFSET: 시간대 변경
SELECT SWITCHOFFSET(SYSDATETIMEOFFSET(), '+09:00');
🔄 7. 실무 변환 예제
실제 업무에서 자주 사용되는 날짜 처리를 각 데이터베이스별로 구현해보겠습니다.
💼 예제 시나리오: 고객 가입 기간 및 생일 분석
고객의 나이, 가입 기간, 다음 생일까지의 일수를 계산하는 쿼리를 각 데이터베이스별로 작성해보겠습니다.
Oracle 버전
SELECT
customer_id,
customer_name,
birth_date,
register_date,
-- 나이 계산
FLOOR(MONTHS_BETWEEN(SYSDATE, birth_date) / 12) AS age,
-- 가입 기간 (일)
FLOOR(SYSDATE - register_date) AS days_since_registration,
-- 가입 기간 (월)
FLOOR(MONTHS_BETWEEN(SYSDATE, register_date)) AS months_since_registration,
-- 이번 년도 생일
ADD_MONTHS(birth_date,
FLOOR(MONTHS_BETWEEN(SYSDATE, birth_date) / 12) * 12) AS this_year_birthday,
-- 다음 생일까지 일수
CASE
WHEN ADD_MONTHS(birth_date,
FLOOR(MONTHS_BETWEEN(SYSDATE, birth_date) / 12) * 12) >= TRUNC(SYSDATE)
THEN ADD_MONTHS(birth_date,
FLOOR(MONTHS_BETWEEN(SYSDATE, birth_date) / 12) * 12) - TRUNC(SYSDATE)
ELSE ADD_MONTHS(birth_date,
FLOOR(MONTHS_BETWEEN(SYSDATE, birth_date) / 12) * 12 + 12) - TRUNC(SYSDATE)
END AS days_to_birthday,
-- 분기별 가입 정보
'Q' || TO_CHAR(register_date, 'Q') || ' ' || TO_CHAR(register_date, 'YYYY') AS register_quarter
FROM customers;
MySQL 버전
SELECT
customer_id,
customer_name,
birth_date,
register_date,
-- 나이 계산
TIMESTAMPDIFF(YEAR, birth_date, NOW()) AS age,
-- 가입 기간 (일)
DATEDIFF(NOW(), register_date) AS days_since_registration,
-- 가입 기간 (월)
TIMESTAMPDIFF(MONTH, register_date, NOW()) AS months_since_registration,
-- 이번 년도 생일
DATE(CONCAT(YEAR(NOW()), '-',
LPAD(MONTH(birth_date), 2, '0'), '-',
LPAD(DAY(birth_date), 2, '0'))) AS this_year_birthday,
-- 다음 생일까지 일수
CASE
WHEN DATE(CONCAT(YEAR(NOW()), '-',
LPAD(MONTH(birth_date), 2, '0'), '-',
LPAD(DAY(birth_date), 2, '0'))) >= CURDATE()
THEN DATEDIFF(
DATE(CONCAT(YEAR(NOW()), '-',
LPAD(MONTH(birth_date), 2, '0'), '-',
LPAD(DAY(birth_date), 2, '0'))),
CURDATE())
ELSE DATEDIFF(
DATE(CONCAT(YEAR(NOW()) + 1, '-',
LPAD(MONTH(birth_date), 2, '0'), '-',
LPAD(DAY(birth_date), 2, '0'))),
CURDATE())
END AS days_to_birthday,
-- 분기별 가입 정보
CONCAT('Q', QUARTER(register_date), ' ', YEAR(register_date)) AS register_quarter
FROM customers;
MS SQL Server 버전
SELECT
customer_id,
customer_name,
birth_date,
register_date,
-- 나이 계산
DATEDIFF(year, birth_date, GETDATE()) -
CASE WHEN DATEADD(year, DATEDIFF(year, birth_date, GETDATE()), birth_date) > GETDATE()
THEN 1 ELSE 0 END AS age,
-- 가입 기간 (일)
DATEDIFF(day, register_date, GETDATE()) AS days_since_registration,
-- 가입 기간 (월)
DATEDIFF(month, register_date, GETDATE()) AS months_since_registration,
-- 이번 년도 생일
DATEFROMPARTS(YEAR(GETDATE()), MONTH(birth_date), DAY(birth_date)) AS this_year_birthday,
-- 다음 생일까지 일수
CASE
WHEN DATEFROMPARTS(YEAR(GETDATE()), MONTH(birth_date), DAY(birth_date)) >= CAST(GETDATE() AS DATE)
THEN DATEDIFF(day, CAST(GETDATE() AS DATE),
DATEFROMPARTS(YEAR(GETDATE()), MONTH(birth_date), DAY(birth_date)))
ELSE DATEDIFF(day, CAST(GETDATE() AS DATE),
DATEFROMPARTS(YEAR(GETDATE()) + 1, MONTH(birth_date), DAY(birth_date)))
END AS days_to_birthday,
-- 분기별 가입 정보
'Q' + CAST(DATEPART(QUARTER, register_date) AS VARCHAR) + ' ' +
CAST(YEAR(register_date) AS VARCHAR) AS register_quarter
FROM customers;
⚡ 8. 성능 고려사항
날짜 함수 사용 시 성능을 고려해야 할 중요한 포인트들입니다.
🚀 인덱스 활용 팁
-- ❌ 나쁜 예: 함수를 인덱스된 열에 사용
SELECT * FROM orders
WHERE YEAR(order_date) = 2025;
-- ✅ 좋은 예: 범위 조건 사용
SELECT * FROM orders
WHERE order_date >= '2025-01-01'
AND order_date < '2026-01-01';
📊 대용량 데이터 처리
-- 파티셔닝을 고려한 날짜 조건
SELECT * FROM large_table
WHERE date_column >= DATEADD(month, -1, GETDATE())
AND date_column < GETDATE();
-- 적절한 날짜 인덱스 생성
CREATE INDEX IX_orders_date
ON orders(order_date)
INCLUDE (customer_id, total_amount);
🔧 최적화 기법
- 날짜 리터럴 사용: 문자열보다 날짜 리터럴 사용
- 함수 인덱스: 자주 사용하는 날짜 함수에 대해 함수 기반 인덱스 생성
- 파티셔닝: 대용량 테이블의 날짜 기반 파티셔닝
- 계산된 열: 복잡한 날짜 계산은 계산된 열로 저장
🎯 9. 마이그레이션 가이드
데이터베이스 간 마이그레이션 시 날짜 함수 변환을 위한 실용적인 가이드입니다.
마이그레이션 방향 | 주요 변경사항 | 주의사항 |
---|---|---|
Oracle → MySQL |
• SYSDATE → NOW() • TO_CHAR → DATE_FORMAT • ADD_MONTHS → DATE_ADD • TRUNC → DATE 함수 |
• Oracle의 날짜 연산(+/-) 불가 • 포맷 문자열 완전히 다름 • DUAL 테이블 불필요 |
Oracle → MS SQL |
• SYSDATE → GETDATE() • TO_CHAR → FORMAT • ADD_MONTHS → DATEADD • MONTHS_BETWEEN → DATEDIFF |
• 날짜 연산 방식 완전 변경 • FROM dual 제거 필요 • 데이터 타입 매핑 주의 |
MySQL → MS SQL |
• NOW() → GETDATE() • DATE_FORMAT → FORMAT • DATE_ADD → DATEADD • CURDATE() → CAST(GETDATE() AS DATE) |
• LIMIT → TOP 변환 • 백틱(`) → 대괄호[] 변환 • 자동 형변환 차이 |
🛠️ 마이그레이션 도구 추천
- AWS SCT (Schema Conversion Tool): 다양한 DB 간 스키마 변환
- Microsoft SSMA: Oracle/MySQL → SQL Server 마이그레이션
- DBConvert: 상용 마이그레이션 도구
- 정규식 기반 스크립트: 단순 함수 변환용
📚 10. 실무 베스트 프랙티스
✅ 권장사항
- 표준 SQL 우선 사용: CURRENT_TIMESTAMP, EXTRACT 등
- 명시적 형변환: 암시적 변환에 의존하지 말 것
- 타임존 고려: 글로벌 서비스 시 UTC 기준 사용
- 일관된 형식: 프로젝트 내 날짜 형식 통일
- NULL 처리: 날짜 계산 시 NULL 값 고려
❌ 피해야 할 것들
- 문자열 날짜 연산: '2025-01-01' + 1 같은 연산
- 하드코딩된 형식: 지역별 설정에 의존하는 형식
- 복잡한 중첩 함수: 가독성과 성능 저하
- 인덱스 컬럼 함수 적용: WHERE YEAR(date) = 2025
- 윤년 무시: 2월 29일 처리 주의
🔍 테스트 체크리스트
- 윤년 처리: 2월 29일 관련 로직
- 월말 처리: 31일 → 30일 변환
- 타임존 변환: 서머타임 적용
- NULL 값 처리: 날짜가 NULL인 경우
- 경계값 테스트: 년도 변경, 월 변경 시점