명령어/DB
[PostgreSQL] PL/pgSQL 함수 로직을 DB 안에 넣어 한 번에 굴린다
jykim23
2026. 8. 2. 19:45
반응형
설치·접속: PostgreSQL 설치와 접속
부제: 결제 금액대별 할인율 같은 분기 로직을 애플리케이션마다 중복 구현하지 말고, DB 함수 하나로 만들어 SQL에서 컬럼처럼 불러 쓰고 싶을 때
-- 조건 분기가 든 계산을 함수로 캡슐화한다
CREATE FUNCTION m10_discount(total numeric) RETURNS numeric AS $$
DECLARE rate numeric;
BEGIN
IF total >= 1000 THEN rate := 0.20;
ELSIF total >= 500 THEN rate := 0.10;
ELSIF total >= 100 THEN rate := 0.05;
ELSE rate := 0;
END IF;
RETURN round(total * (1 - rate), 2);
END;
$$ LANGUAGE plpgsql IMMUTABLE;
SELECT t AS 결제금액, m10_discount(t) AS 할인후
FROM (VALUES (50),(120),(600),(1500)) v(t);
결제금액 | 할인후
----------+---------
50 | 50.00
120 | 114.00
600 | 540.00
1500 | 1200.00
(4 rows)PL/pgSQL은 PostgreSQL에 내장된 절차형 언어로, 변수 선언·조건 분기·반복문 같은 "코드"를 DB 안에서 실행한다. 순수 SQL로는 표현하기 껄끄러운 분기 로직을 함수로 감싸 두면, 위처럼 SELECT 안에서 일반 함수처럼 호출할 수 있다. 로직이 DB 한 곳에 모이므로 여러 애플리케이션이 같은 규칙을 공유하고, 계산이 데이터 옆에서 돌아 왕복(round-trip)도 줄어든다. 부작용이 없는 함수엔 IMMUTABLE을 붙여 옵티마이저가 결과를 재사용하게 한다.
이렇게도 쓴다
여러 행을 반환하는 함수를 만든다. (조합: RETURNS TABLE + RETURN QUERY)
CREATE FUNCTION m10_user_summary(min_qty int)
RETURNS TABLE(user_id int, orders bigint, total_qty bigint) AS $$
BEGIN
RETURN QUERY
SELECT o.user_id, count(*), sum(o.qty)::bigint
FROM orders o GROUP BY o.user_id
HAVING sum(o.qty) >= min_qty ORDER BY sum(o.qty) DESC;
END; $$ LANGUAGE plpgsql;
SELECT * FROM m10_user_summary(15); -- 테이블처럼 FROM에 넣어 쓴다
반복문으로 여러 행을 순회한다. (조합: FOR ... IN LOOP)
DO $$
DECLARE r record;
BEGIN
FOR r IN SELECT id, price FROM products WHERE price > 200 LOOP
RAISE NOTICE '상품 % 가격 %', r.id, r.price;
END LOOP;
END $$;
예외를 잡아 안전하게 처리한다. (조합: EXCEPTION)
CREATE FUNCTION m10_safe_div(a numeric, b numeric) RETURNS numeric AS $$
BEGIN
RETURN a / b;
EXCEPTION WHEN division_by_zero THEN RETURN NULL;
END; $$ LANGUAGE plpgsql;
쿼리 한 건만 감싸는 가벼운 함수는 SQL 함수로 충분하다. (조합: LANGUAGE sql)
CREATE FUNCTION m10_order_cnt(uid int) RETURNS bigint AS $$
SELECT count(*) FROM orders WHERE user_id = uid;
$$ LANGUAGE sql STABLE;
함수 목록·정의를 확인한다. (조합: \df)
\df m10_* -- 함수 시그니처 목록
\sf m10_discount -- 함수 정의 소스 보기반응형