많은 프로그래밍 언어에서는 함수와 프로시저가 거의 차이가 없어. 근데 SQL에서는 다르지. PostgreSQL에서 함수와 프로시저는 단순히 코드를 실행하는 두 가지 방법이 아니야. 이건 완전히 다른 사고방식이야.
SQL에서 함수는 DB의 데이터를 바꿀 수 없어. 전달받은 데이터만 가지고 결과를 반환해야 해. 주로 SELECT 쿼리 안에서 쓰려고 만든 거야.
SQL에서 프로시저는 DB를 바꾸려고 만들어진 거야. 그래서 트랜잭션을 다룰 수 있고(함수랑 다르게), DB에 뭔가를 쓸 수도 있어. 그리고 SELECT 쿼리 안에서는 쓸 수 없어.
간단하게 비교해보면 이래:
| 특징 | 함수 (FUNCTION) | 프로시저 (PROCEDURE) |
|---|---|---|
| 데이터 반환 | ✅ 응 (RETURNS ...) |
❌ 아니 (동작만 함) |
| 호출 방법 | SELECT, PERFORM |
CALL |
| 쿼리에서 사용 가능 | ✅ 응 | ❌ 아니 |
DO에서 사용 가능 |
✅ 응 | ❌ 아니 |
COMMIT, ROLLBACK 지원 |
❌ 아니 | ✅ 응 |
| PostgreSQL에 도입된 시점 | 처음부터 | 11버전부터 |
SQL에서의 차이
일반 SQL에서 함수는 표현식이랑 비슷해: 계산해서 값을 반환하지. 프로시저는 명령문이야: 뭔가를 실행하지만 표현식에는 못 써.
SQL에서 함수
SELECT calculate_discount(200);
WHERE,ORDER BY,INSERT,UPDATE등에서 쓸 수 있어.- 순수해야 해: DB 상태를 바꾸면 안 됨(IMMUTABLE/STABLE이면).
SQL에서 프로시저
CALL process_order(123);
- 결과를 반환하지 않아.
COMMIT,ROLLBACK,RAISE호출, 루프 실행 가능.
PL/pgSQL에서의 차이
PostgreSQL의 함수는 계산 묶음이라고 생각하면 돼. 엄청 유연해서 파라미터 전달, 조건문, 루프, 커서, 서브쿼리, 행/스칼라/테이블 반환 다 가능해.
PL/pgSQL 함수
CREATE FUNCTION square(x INT) RETURNS INT AS $$
BEGIN
RETURN x * x;
END;
$$ LANGUAGE plpgsql;
특징:
RETURNS필수DECLARE,BEGIN,END,LOOP,IF,CASE사용 가능COMMIT/ROLLBACK불가SELECT,UPDATE,CHECK,WHERE,RETURNING에서 호출 가능
호출 예시:
SELECT square(5); -- 25 반환
PL/pgSQL 프로시저
프로시저는 동작 제어 메커니즘이야. 이런 상황에 딱이야:
- 여러 단계의 로직 실행할 때;
- 대량 데이터 업데이트/삽입할 때;
- 트랜잭션 제어 필요할 때:
COMMIT,ROLLBACK,SAVEPOINT.
CREATE PROCEDURE log_event(msg TEXT) AS $$
BEGIN
INSERT INTO logs(message) VALUES (msg);
COMMIT;
END;
$$ LANGUAGE plpgsql;
특징:
RETURNS없음CALL로만 호출COMMIT,ROLLBACK,SAVEPOINT사용 가능- 배치 처리, 마이그레이션, ETL에 적합
호출 예시:
CALL log_event('처리 완료');
함수와 프로시저가 나뉜 이유
왜냐면 SQL에서 목적이 다르거든:
| 함수 | 프로시저 |
|---|---|
| "뭔가 계산해서 반환" | "뭔가 실행하고 결과는 반환 안 함" |
| SQL에서 호출 | 명령어처럼 호출 |
| 트랜잭션 제어 불가 | 트랜잭션 제어 가능 |
SELECT, JOIN, WHERE에서 사용 |
CALL, 스크립트에서 사용 |
프로시저의 핵심 장점 — COMMIT
프로시저는 자기 안에서 트랜잭션을 제어할 수 있어. 즉, 프로시저 안에서 바로 이렇게 할 수 있음:
BEGIN;
-- 로직
SAVEPOINT point1;
-- 업데이트 시도
ROLLBACK TO point1;
COMMIT;
그리고 함수에서는 COMMIT이랑 ROLLBACK이 금지야. 만약 시도하면: ERROR: invalid transaction termination in function 이런 에러 나옴
즉, 함수는 반드시 결정적이고 안전해야 하고, 프로시저는 "더러운 일"도 할 수 있어 — 정리, 로그, 삽입 등.
비교 표
| 특징 | FUNCTION |
PROCEDURE |
|---|---|---|
| 값 반환 | ✅ RETURNS |
❌ |
SELECT에서 사용 |
✅ | ❌ |
| 호출 방법 | SELECT, PERFORM, DO |
오직 CALL |
| 트리거에서 사용 가능 | ✅ | ❌ (함수만 가능) |
내부 트랜잭션 (COMMIT) |
❌ 금지 | ✅ 허용 |
| OUT 파라미터 사용 | RETURNS TABLE, RECORD로 |
OUT 파라미터 직접 사용 |
| 계산에 적합 | ✅ | 🚫 적합하지 않음 |
| ETL, 로딩에 적합 | 🚫 제한적 | ✅ 완전 적합 |
| 커서 사용 가능 | ✅ 응 | ✅ 응 |
언제 뭘 써야 할까?
함수를 써야 할 때:
- 반환값이 필요할 때;
SELECT에서 호출하거나, 데이터 필터링할 때;- 간단한 계산, 체크, SQL 래퍼일 때.
프로시저를 써야 할 때:
- 복잡한 동작을 해야 할 때;
- 트랜잭션 제어가 필요할 때;
- 배치 처리, 데이터 이동, 아카이브, 로깅 등 할 때.
GO TO FULL VERSION