Functions Basics
9 examples to get you started with PostgreSQL functions - 6 basic and 3 intermediate.
Search across all documentation pages
9 examples to get you started with PostgreSQL functions - 6 basic and 3 intermediate.
CREATE on schema app.
# Local dev with Docker (PostgreSQL 18)
docker run -d --name pg18 -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:18
psql postgres://postgres:dev@localhost:5432/postgresSingle SELECT expression body.
CREATE OR REPLACE FUNCTION app.account_order_count(p_account_id bigint)
RETURNS bigint
LANGUAGE sql
STABLE
AS $$
SELECT count(*) FROM app.orders WHERE account_id = p_account_id;
$$;Variables and IF/LOOP.
CREATE OR REPLACE FUNCTION app.is_vip(p_account_id bigint)
RETURNS boolean
LANGUAGE plpgsql
STABLE
AS $$
DECLARE v_total numeric;
BEGIN
SELECT coalesce(sum(total),0) INTO v_total FROM app.orders WHERE account_id = p_account_id;
RETURN v_total > 10000;
END;
$$;Use in SELECT and WHERE.
SELECT id, app.account_order_count(id) AS orders FROM app.accounts;Set-returning function shape.
CREATE OR REPLACE FUNCTION app.big_orders(p_min numeric)
RETURNS TABLE (order_id bigint, total numeric)
LANGUAGE sql
STABLE
AS $$
SELECT id, total FROM app.orders WHERE total >= p_min;
$$;Pure functions enable index expressions.
CREATE OR REPLACE FUNCTION app.norm_email(e text)
RETURNS text
LANGUAGE sql
IMMUTABLE
AS $$ SELECT lower(trim(e)); $$;CREATE OR REPLACE updates body.
CREATE OR REPLACE FUNCTION app.norm_email(e text) RETURNS text
LANGUAGE sql IMMUTABLE AS $$ SELECT lower(trim(e)); $$;Procedures allow transaction control (CALL).
CREATE OR REPLACE PROCEDURE app.archive_old_orders()
LANGUAGE plpgsql
AS $$
BEGIN
DELETE FROM app.orders WHERE created_at < now() - interval '2 years';
END;
$$;
CALL app.archive_old_orders();Default runs as caller.
ALTER FUNCTION app.account_order_count(bigint) SECURITY INVOKER;Harden privileged functions.
CREATE OR REPLACE FUNCTION app.admin_stat()
RETURNS int LANGUAGE sql SECURITY DEFINER
SET search_path = pg_catalog, pg_temp
AS $$ SELECT 1; $$;Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, and PostGIS 3.5+.
Reviewed by Chris St. John·Last updated Jul 16, 2026