Stored Procedures
A stored procedure is a named block of procedural SQL logic — variables, conditionals, loops, multiple statements — that is saved inside the database itself and invoked by name instead of being sent from the application every time. Rather than an application executing a sequence of separate SQL statements over the network, it can call one procedure and let the database run the whole sequence internally.
Why stored procedures exist
Reuse — the same logic can be called from many applications or scripts without duplicating SQL in each one.
Reduced network round-trips — several statements execute inside the database in one call instead of many separate queries from the client.
Centralized business rules — logic like "when placing an order, deduct stock and record the transaction" lives in one place, next to the data it operates on.
Access control — a user can be granted permission to execute a procedure without being granted direct access to the underlying tables it touches.
Basic syntax: CREATE PROCEDURE and CALL
The exact syntax differs by database, but the shape is consistent: define the procedure once with CREATE PROCEDURE, then invoke it repeatedly with CALL (or EXEC, depending on the dialect). Here is a simple PostgreSQL example that records a payment and marks an invoice as paid:
PostgreSQL: a procedure with an input parameter
CREATE PROCEDURE mark_invoice_paid(p_invoice_id INT, p_amount NUMERIC) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO payments (invoice_id, amount, paid_at) VALUES (p_invoice_id, p_amount, NOW()); UPDATE invoices SET status = 'paid' WHERE invoice_id = p_invoice_id; END; $$; -- Invoke it CALL mark_invoice_paid(1001, 249.99);
Both the INSERT and the UPDATE run as part of a single call, and (depending on how the procedure and surrounding transaction are configured) either both succeed or neither does, keeping payments and invoices consistent without the application needing to orchestrate two separate statements itself.
A procedure with conditional logic
PostgreSQL: branching inside a procedure
CREATE PROCEDURE apply_late_fee(p_invoice_id INT)
LANGUAGE plpgsql
AS $$
DECLARE
v_due_date DATE;
BEGIN
SELECT due_date INTO v_due_date FROM invoices WHERE invoice_id = p_invoice_id;
IF v_due_date < CURRENT_DATE THEN
UPDATE invoices
SET amount_due = amount_due * 1.05
WHERE invoice_id = p_invoice_id;
END IF;
END;
$$;
CALL apply_late_fee(1001);Dialect | Define | Invoke | Procedural language |
|---|---|---|---|
PostgreSQL | CREATE PROCEDURE ... LANGUAGE plpgsql | CALL procedure_name(...) | PL/pgSQL |
SQL Server | CREATE PROCEDURE ... AS BEGIN ... END | EXEC procedure_name ... | T-SQL |
Oracle | CREATE OR REPLACE PROCEDURE ... IS BEGIN ... END | BEGIN procedure_name(...); END; | PL/SQL |
MySQL | CREATE PROCEDURE ... BEGIN ... END | CALL procedure_name(...) | SQL/PSM extensions |
Stored procedures are not free of downsides: they move business logic out of version-controlled application code and into the database, which can make testing, code review, and deployment pipelines less straightforward unless the team has good tooling for versioning database objects. They are most valuable for logic that must run close to the data for performance or consistency reasons, or that needs to be shared across multiple applications written in different languages.
A stored procedure is a named, reusable, procedural block of SQL logic saved in the database.
CREATE PROCEDURE defines it; CALL (or EXEC in T-SQL) invokes it.
Benefits include reuse, fewer network round-trips, centralized logic, and finer-grained access control.
Procedural syntax is not standardized — PL/pgSQL, T-SQL, and PL/SQL each differ significantly.