Chapter 7: Stored Procedures and Functions
September 28, 2025 ยท View on GitHub
Stored Procedures
Stored procedures are SQL code saved in the database that can be called repeatedly.
Basic Syntax
DELIMITER //
CREATE PROCEDURE GetCustomerOrders(IN customer_id INT)
BEGIN
SELECT * FROM orders
WHERE customer_id = customer_id
ORDER BY order_date DESC;
END //
DELIMITER ;
-- Call procedure
CALL GetCustomerOrders(123);
Parameters
DELIMITER //
CREATE PROCEDURE UpdateProductPrice(
IN product_id INT,
IN new_price DECIMAL(10,2),
OUT old_price DECIMAL(10,2)
)
BEGIN
SELECT price INTO old_price
FROM products WHERE id = product_id;
UPDATE products
SET price = new_price
WHERE id = product_id;
END //
DELIMITER ;
-- Call with OUT parameter
CALL UpdateProductPrice(1, 19.99, @old);
SELECT @old;
Control Flow
DELIMITER //
CREATE PROCEDURE ProcessOrder(
IN order_id INT,
OUT status_message VARCHAR(100)
)
BEGIN
DECLARE order_total DECIMAL(10,2);
DECLARE customer_credit DECIMAL(10,2);
SELECT total INTO order_total
FROM orders WHERE id = order_id;
IF order_total IS NULL THEN
SET status_message = 'Order not found';
ELSEIF order_total > 1000 THEN
SET status_message = 'Large order - requires approval';
ELSE
SET status_message = 'Order processed';
UPDATE orders SET status = 'confirmed'
WHERE id = order_id;
END IF;
END //
DELIMITER ;
Loops
DELIMITER //
CREATE PROCEDURE GenerateReport()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE prod_id INT;
DECLARE prod_name VARCHAR(100);
DECLARE cur CURSOR FOR
SELECT id, name FROM products;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO prod_id, prod_name;
IF done THEN
LEAVE read_loop;
END IF;
-- Process each product
INSERT INTO report_temp (product_id, product_name)
VALUES (prod_id, prod_name);
END LOOP;
CLOSE cur;
END //
DELIMITER ;
Stored Functions
Functions return a single value and can be used in SQL statements.
DELIMITER //
CREATE FUNCTION CalculateDiscount(
total DECIMAL(10,2),
customer_type VARCHAR(20)
)
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
DECLARE discount DECIMAL(10,2);
IF customer_type = 'gold' THEN
SET discount = total * 0.20;
ELSEIF customer_type = 'silver' THEN
SET discount = total * 0.10;
ELSE
SET discount = 0;
END IF;
RETURN discount;
END //
DELIMITER ;
-- Use in query
SELECT
order_id,
total,
CalculateDiscount(total, customer_type) AS discount
FROM orders;
Triggers
Triggers automatically execute in response to table events.
DELIMITER //
CREATE TRIGGER update_inventory
AFTER INSERT ON order_items
FOR EACH ROW
BEGIN
UPDATE products
SET stock_quantity = stock_quantity - NEW.quantity
WHERE id = NEW.product_id;
END //
CREATE TRIGGER audit_price_changes
BEFORE UPDATE ON products
FOR EACH ROW
BEGIN
IF OLD.price != NEW.price THEN
INSERT INTO price_audit (product_id, old_price, new_price, changed_at)
VALUES (NEW.id, OLD.price, NEW.price, NOW());
END IF;
END //
DELIMITER ;
Error Handling
DELIMITER //
CREATE PROCEDURE SafeTransfer(
IN from_account INT,
IN to_account INT,
IN amount DECIMAL(10,2)
)
BEGIN
DECLARE exit handler for SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT 'Transfer failed' AS error_message;
END;
START TRANSACTION;
UPDATE accounts
SET balance = balance - amount
WHERE id = from_account;
UPDATE accounts
SET balance = balance + amount
WHERE id = to_account;
COMMIT;
SELECT 'Transfer successful' AS message;
END //
DELIMITER ;
Managing Procedures and Functions
-- List procedures
SHOW PROCEDURE STATUS;
-- View procedure code
SHOW CREATE PROCEDURE procedure_name;
-- Drop procedure
DROP PROCEDURE IF EXISTS procedure_name;
-- List functions
SHOW FUNCTION STATUS;
-- Drop function
DROP FUNCTION IF EXISTS function_name;
Best Practices
- Use meaningful names for procedures and parameters
- Add comments to complex logic
- Handle errors appropriately
- Keep procedures focused on single tasks
- Test thoroughly before production
- Consider performance - procedures aren't always faster