A stored procedure bundles reusable logic — loops, conditionals, multiple statements — stored and run inside the database itself, called with CALL. Unlike the SQL standard covered in every other topic in this reference, procedural syntax is NOT portable: PostgreSQL uses PL/pgSQL, MySQL has its own procedural extension, and SQL Server uses T-SQL, and code written for one will not run unmodified on another.
A basic PL/pgSQL procedure (PostgreSQL)
PL/pgSQL adds real procedural control flow — variables, IF, loops — on top of standard SQL, executed entirely inside the database.
CREATE PROCEDURE apply_discount(product_id INTEGER, percent NUMERIC)
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE products
SET price = price * (1 - percent / 100)
WHERE id = product_id;
END;
$$;
CALL apply_discount(5, 10); -- 10% off product 5Functions vs. procedures
A function returns a value and can be used inside a query (like SELECT my_function(x)); a procedure (added to the SQL standard more recently, and to PostgreSQL in version 11) doesn't return a value directly and is invoked with CALL. Which one to use depends on whether the logic needs to plug into a larger query or stands alone as an action.
CREATE FUNCTION full_name(first TEXT, last TEXT) RETURNS TEXT
LANGUAGE plpgsql
AS $$
BEGIN
RETURN first || ' ' || last;
END;
$$;
SELECT full_name(first_name, last_name) FROM users; -- usable inside a SELECTNot portable across engines
Unlike the SELECT/JOIN/WHERE syntax covered throughout the rest of this reference — which is close to identical across engines — procedural code is genuinely engine-specific. A PL/pgSQL procedure will not run on MySQL, and MySQL's stored procedure syntax will not run on SQL Server's T-SQL, without a real rewrite.