Oracle 함수 그대로 쳤다가 에러 폭탄 맞은 썰 (PostgreSQL 함수 완벽 변환표)
테스트 환경: PostgreSQL 17, Rocky Linux 9 / Oracle 19c 비교 기준
PostgreSQL로 넘어온 첫 주, 저는 습관적으로 이렇게 쳤습니다.
SELECT NVL(bonus, 0) FROM employees;
그리고 돌아온 답: ERROR: function nvl(numeric, integer) does not exist
"어? NVL이 없다고?"
당황해서 DECODE를 쳤더니 또 에러. SYSDATE를 쳤더니 또 에러. FROM dual을 붙였더니 이번엔 dual 테이블이 없다네요. 그야말로 에러 폭탄이었습니다.
Oracle DBA가 PostgreSQL에서 처음 마주하는 벽이 바로 이겁니다. SQL은 표준이라 대부분 통하는데, 손에 붙은 Oracle 전용 함수들이 하나씩 배신합니다. NVL, DECODE, SYSDATE... 15년간 아무 생각 없이 쳤던 것들이죠. 이 글은 그 에러 폭탄을 미리 피하는 지도입니다.
이번 편은 입문 시리즈(1~4편)를 마치고 시작하는 실무 편의 첫 글입니다. Oracle 함수를 PostgreSQL로 옮기는 실전 변환표와, Oracle엔 없는 PostgreSQL 함수들을 다룹니다.
- ★ Oracle → PostgreSQL 함수 완벽 변환표
- NVL vs COALESCE, DECODE vs CASE (가장 많이 검색됨)
- 문자열·날짜·숫자 함수 비교
- Oracle엔 없는 PostgreSQL 함수들
- 조인·서브쿼리 차이
바쁘신 분은 변환표부터 보세요. 북마크해두면 두고두고 씁니다.
왜 함수가 다를까
잠깐 짚고 갑시다. SQL 표준(ANSI SQL)에 정의된 함수는 양쪽 다 됩니다. 문제는 각 DB가 표준 외에 자기만의 편의 함수를 만들었다는 겁니다.
NVL, DECODE, SYSDATE → Oracle이 만든 전용 함수
COALESCE, CASE → SQL 표준 (양쪽 다 됨)
재밌는 건, PostgreSQL은 표준을 더 충실히 따른다는 점입니다. 그래서 Oracle 전용 함수 대신 표준 함수를 쓰면 됩니다. 익숙해지면 오히려 다른 DB에서도 통하는 표준 SQL 습관이 생깁니다.
★ Oracle → PostgreSQL 함수 변환표 (북마크 필수)
실무에서 매일 쓰는 함수 위주로 정리했습니다. 이 표 하나면 대부분 해결됩니다.
NULL 처리 함수
Oracle PostgreSQL 설명
| NVL(a, b) | COALESCE(a, b) | NULL이면 b |
| NVL2(a, b, c) | CASE WHEN a IS NOT NULL THEN b ELSE c END | 3항 |
| NULLIF(a, b) | NULLIF(a, b) | 같으면 NULL (동일!) |
| COALESCE(...) | COALESCE(...) | 동일 (표준) |
-- Oracle
SELECT NVL(bonus, 0) FROM employees;
-- PostgreSQL
SELECT COALESCE(bonus, 0) FROM employees;
꿀팁: COALESCE는 인자를 여러 개 받습니다. COALESCE(a, b, c, 0) → 앞에서부터 NULL 아닌 첫 값. NVL보다 강력합니다.
조건 함수
Oracle PostgreSQL 설명
| DECODE(x, 1, 'A', 2, 'B', 'C') | CASE x WHEN 1 THEN 'A' WHEN 2 THEN 'B' ELSE 'C' END | 조건 분기 |
| CASE ... | CASE ... | 동일 (표준) |
-- Oracle의 DECODE
SELECT DECODE(dept_id, 10, '영업', 20, '개발', '기타') FROM emp;
-- PostgreSQL은 CASE로 (더 읽기 쉬움)
SELECT CASE dept_id
WHEN 10 THEN '영업'
WHEN 20 THEN '개발'
ELSE '기타'
END
FROM employees;
DECODE가 없어서 처음엔 불편하지만, CASE가 더 읽기 쉽고 표준이라 금방 적응됩니다.
날짜 함수 (가장 많이 헷갈림)
Oracle PostgreSQL 설명
| SYSDATE | CURRENT_TIMESTAMP 또는 now() | 현재 시각 |
| SYSDATE (날짜만) | CURRENT_DATE | 오늘 날짜 |
| ADD_MONTHS(d, n) | d + INTERVAL 'n months' | 월 더하기 |
| MONTHS_BETWEEN(a, b) | EXTRACT(...) 조합 | 월 차이 |
| LAST_DAY(d) | (date_trunc('month', d) + INTERVAL '1 month - 1 day') | 말일 |
| TRUNC(d) | date_trunc('day', d) | 날짜 절삭 |
| TO_CHAR(d, 'YYYY-MM-DD') | TO_CHAR(d, 'YYYY-MM-DD') | 동일! |
| TO_DATE(s, fmt) | TO_DATE(s, fmt) | 동일! |
-- Oracle: 3개월 후
SELECT ADD_MONTHS(SYSDATE, 3) FROM dual;
-- PostgreSQL: INTERVAL 사용 (더 직관적)
SELECT CURRENT_TIMESTAMP + INTERVAL '3 months';
충격 포인트: PostgreSQL엔 DUAL 테이블이 없습니다! FROM dual을 빼면 됩니다.
-- Oracle
SELECT 1 + 1 FROM dual;
-- PostgreSQL (FROM 자체가 불필요)
SELECT 1 + 1;
INTERVAL은 처음엔 낯설지만 '3 months', '2 days', '1 year 6 months'처럼 자연어에 가까워서 익숙해지면 더 편합니다.
문자열 함수
Oracle PostgreSQL 설명
| SUBSTR(s, 1, 3) | SUBSTR(s, 1, 3) | 동일 |
| INSTR(s, 'x') | POSITION('x' IN s) | 위치 찾기 |
| LENGTH(s) | LENGTH(s) | 동일 |
| a || b | a || b | 문자열 연결 (동일!) |
| CONCAT(a, b) | CONCAT(a, b) | 동일 |
| LPAD/RPAD | LPAD/RPAD | 동일 |
| TRIM/LTRIM/RTRIM | TRIM/LTRIM/RTRIM | 동일 |
| UPPER/LOWER | UPPER/LOWER | 동일 |
| REPLACE | REPLACE | 동일 |
문자열 함수는 대부분 똑같습니다! INSTR 정도만 POSITION으로 바뀝니다.
숫자·형변환 함수
Oracle PostgreSQL 설명
| ROUND(n, 2) | ROUND(n, 2) | 동일 |
| TRUNC(n, 2) | TRUNC(n, 2) | 동일 |
| MOD(a, b) | MOD(a, b) 또는 a % b | 나머지 |
| TO_NUMBER(s) | s::numeric 또는 CAST(s AS numeric) | 형변환 |
| TO_CHAR(n) | n::text 또는 TO_CHAR(n, fmt) | 문자로 |
PostgreSQL의 강점: :: 형변환 연산자가 편합니다.
-- Oracle
SELECT TO_NUMBER('123') FROM dual;
-- PostgreSQL (:: 캐스팅이 간결)
SELECT '123'::numeric;
★ Oracle엔 없는 PostgreSQL 함수들
이제 반대로, Oracle 사용자가 부러워할 PostgreSQL만의 함수들입니다.
1. STRING_AGG - 행을 한 줄로 합치기
-- 부서별 직원 이름을 콤마로 연결!
SELECT dept_id, STRING_AGG(emp_name, ', ') AS members
FROM employees
GROUP BY dept_id;
-- 결과:
-- 10 | 홍길동, 김철수, 이영희
-- 20 | 박개발, 최코딩
Oracle의 LISTAGG에 해당하지만 더 유연합니다. 정렬도 됩니다: STRING_AGG(emp_name, ', ' ORDER BY emp_name).
2. generate_series - 연속 값 생성
-- 1부터 10까지 숫자 생성
SELECT generate_series(1, 10);
-- 날짜 시리즈도!
SELECT generate_series(
'2026-01-01'::date,
'2026-12-01'::date,
'1 month'::interval
);
Oracle에서 CONNECT BY LEVEL로 힘겹게 만들던 걸 한 방에. 테스트 데이터 만들 때 최고입니다.
3. 배열 함수
-- 배열로 집계
SELECT dept_id, ARRAY_AGG(emp_name) AS names
FROM employees GROUP BY dept_id;
-- 결과: 10 | {홍길동,김철수,이영희}
4. FILTER 절 - 조건부 집계
-- 한 쿼리에서 조건별 카운트!
SELECT
COUNT(*) AS 전체,
COUNT(*) FILTER (WHERE salary > 5000) AS 고연봉,
COUNT(*) FILTER (WHERE dept_id = 10) AS 영업부
FROM employees;
Oracle에서 CASE로 복잡하게 짜던 조건부 집계가 깔끔해집니다.
조인과 서브쿼리 - 이건 거의 같다
다행히 조인과 서브쿼리는 대부분 동일합니다.
조인 (표준 문법 동일)
-- INNER/LEFT/RIGHT/FULL JOIN 모두 동일
SELECT e.emp_name, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;
주의: Oracle의 옛날 (+) 아우터 조인은 PostgreSQL에 없습니다. 표준 LEFT JOIN을 쓰세요.
-- Oracle 옛 스타일 (PostgreSQL 불가)
-- WHERE e.dept_id = d.dept_id(+)
-- 표준 방식 (양쪽 다 됨)
LEFT JOIN departments d ON e.dept_id = d.dept_id
서브쿼리·CTE
-- WITH 절 (CTE)은 양쪽 다 지원, 문법 동일
WITH high_earners AS (
SELECT * FROM employees WHERE salary > 5000
)
SELECT * FROM high_earners WHERE dept_id = 10;
CTE는 8편에서 재귀 쿼리까지 더 깊게 다룹니다.
★ AI로 함수 변환 자동화하기
이 많은 함수를 다 외울 필요 없습니다. Claude를 변환기로 쓰면 됩니다.
프롬프트:
"이 Oracle SQL을 PostgreSQL 17로 변환해줘.
Oracle 전용 함수(NVL, DECODE, SYSDATE 등)를
PostgreSQL 표준으로 바꾸고,
바뀐 함수를 표로 정리해줘.
[Oracle SQL 붙여넣기]"
특히 DECODE가 복잡하게 중첩된 쿼리는 AI가 CASE로 깔끔하게 바꿔줍니다. 손으로 하면 실수하기 쉬운 부분이죠.
꿀팁: 자주 쓰는 변환은 4편에서 소개한 "나만의 변환 노트"에 쌓아두세요. NVL → COALESCE 같은 건 몇 번 하면 그냥 외워집니다.
⚠️ 검증 필수: AI 변환 결과는 반드시 실행해서 확인하세요. 특히 날짜 함수는 결과가 미묘하게 다를 수 있습니다 (예: MONTHS_BETWEEN 계산 방식).
Oracle DBA가 자주 하는 함수 실수 TOP 3
실수 1: NVL을 계속 침
NVL(a, b) -- ❌ 없음
COALESCE(a, b) -- ✅
가장 흔합니다. 손가락이 기억하고 있어서 한동안 계속 칩니다.
실수 2: FROM dual 붙이기
SELECT SYSDATE FROM dual; -- ❌ dual 없음, SYSDATE 없음
SELECT CURRENT_TIMESTAMP; -- ✅ FROM 자체가 불필요
실수 3: 문자열 연결 시 NULL 처리
-- Oracle: NULL을 빈 문자열로 취급
-- PostgreSQL: NULL과 연결하면 결과가 NULL!
SELECT 'Hello' || NULL; -- 결과: NULL (주의!)
SELECT 'Hello' || COALESCE(NULL, ''); -- 안전
이건 정말 조심해야 합니다. Oracle 습관대로 하면 데이터가 통째로 NULL이 될 수 있어요.
마무리
Oracle 함수를 PostgreSQL로 옮기는 핵심입니다.
- NVL → COALESCE — 가장 많이 쓰는 변환
- DECODE → CASE — 더 읽기 쉬움
- SYSDATE → CURRENT_TIMESTAMP — FROM dual도 제거
- PostgreSQL 강점 함수 — STRING_AGG, generate_series, FILTER
- NULL 연결 주의 — ||로 NULL 연결 시 결과 NULL
처음엔 손에 붙은 Oracle 함수가 안 먹혀서 답답하지만, 며칠이면 적응됩니다. 오히려 PostgreSQL은 표준을 충실히 따라서, 여기서 익힌 습관이 다른 DB에서도 통합니다. NVL을 COALESCE로 바꾸는 손가락이 어느새 자연스러워질 겁니다.
다음 편에서는 인덱스를 다룹니다. Oracle엔 없는 GIN, GiST, BRIN 같은 PostgreSQL만의 인덱스가 등장하는데, 이게 PostgreSQL의 진짜 무기 중 하나입니다.
여러분이 겪은 "이 함수가 없어서 당황했다" 경험이나, 발견한 PostgreSQL 꿀함수가 있다면 댓글로 공유해주세요.
'DBA 실무 > PostgreSQL' 카테고리의 다른 글
| [PostgreSQL 시리즈 4편] psql 완벽 활용 + AI로 학습 가속 (0) | 2026.08.19 |
|---|---|
| [PostgreSQL 시리즈 3편] DML과 트랜잭션 - Oracle UNDO vs PostgreSQL MVCC (시리즈 핵심) (0) | 2026.08.17 |
| [PostgreSQL 시리즈 2편] 자료형과 DDL 완벽 비교 - Oracle 타입을 PostgreSQL로 옮기기 (0) | 2026.08.14 |
| PostgreSQL 시리즈 1편] Oracle DBA를 위한 PostgreSQL 첫걸음 - 아키텍처부터 첫 접속까지 (1) | 2026.08.12 |
| PostgreSQL, 설치할 때 정한 걸로 3년을 삽니다 — 설치부터 초기 설정까지 (0) | 2026.08.10 |