PL/pgSQL Business Logic Procedures and Best Practices Trigger: When writing stored procedures or database logic.
Need return value? → Use FUNCTION
Need transaction control? → Use PROCEDURE
Need auto-update? → Use TRIGGER
Need audit trail? → Use trigger with AUDIT_LOG
Need error handling? → Use EXCEPTION block
Encapsulate complex business logic within the database using Stored Procedures and Functions to ensure consistency, reducing round-trips and enforcing data integrity closer to the source.
FUNCTION)READ operations.VOLATILE modifier function).fn_verb_noun (e.g., fn_calculate_risk_score).PROCEDURE)COMMIT or ROLLBACK within the logic (only possible in Procedures).INSERT, UPDATE, DELETE workflows across multiple tables.sp_verb_noun (e.g., sp_register_new_user).Always use CREATE OR REPLACE to allow easy redeployment.
Include a standard header block.
-- =============================================================================
-- PROCEDURE: Register New User
-- AUTHOR: [Name]
-- PURPOSE: Orchestrates user creation, profile setup, and initial logging.
-- =============================================================================
CREATE OR REPLACE PROCEDURE sp_register_new_user(
p_email VARCHAR,
p_password_hash VARCHAR
)
LANGUAGE plpgsql
AS $$
p_ to distinguish them from internal variables (v_) and column names.DECLARE block.v_.v_created_at TIMESTAMP := NOW();Use EXCEPTION blocks to handle errors gracefully or re-raise them with context.
Standardize error messages.
BEGIN
-- Logic here
EXCEPTION
WHEN unique_violation THEN
RAISE EXCEPTION 'User with email % already exists.', p_email;
WHEN OTHERS THEN
RAISE NOTICE 'Unexpected error in sp_register_new_user: %', SQLERRM;
ROLLBACK; -- If applicable
END;
INSERT INTO ... SELECT ...) over looping (FOR r IN SELECT ... LOOP ...) whenever possible.IF ... THEN RETURN; END IF;) to save processing.SECURITY DEFINER functions; they run with the privileges of the creator. Prefer SECURITY INVOKER (default) unless elevation is strictly required.SET search_path = public (or specific schema) in functions to prevent path hijacking.updated_at).-- Example: Auto-update `updated_at` column
CREATE OR REPLACE FUNCTION trg_fn_set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_employee_updated_at
BEFORE UPDATE ON EMPLOYEE
FOR EACH ROW EXECUTE FUNCTION trg_fn_set_updated_at();
For compliance or debugging, log significant DML operations to an audit table.
CREATE TABLE AUDIT_LOG (
id SERIAL PRIMARY KEY,
table_name TEXT NOT NULL,
operation TEXT NOT NULL, -- INSERT, UPDATE, DELETE
old_data JSONB,
new_data JSONB,
changed_by TEXT DEFAULT current_user,
changed_at TIMESTAMP DEFAULT NOW()
);