A stored procedure is a named block of statements run with CALL; it takes parameters, can return result sets and can change data. Its body contains semicolons, and the mysql client splits its input at every ;. Typed without the DELIMITER lines, the procedure below reaches the server in two pieces and fails twice with ERROR 1064 (42000): You have an error in your SQL syntax. DELIMITER // makes the client split on // instead until you switch back:
DELIMITER //
CREATE PROCEDURE customer_orders(IN p_email VARCHAR(255))
READS SQL DATA
BEGIN
SELECT o.id, o.status, o.ordered_at
FROM orders AS o JOIN customers AS c ON c.id = o.customer_id
WHERE c.email = p_email ORDER BY o.ordered_at;
END //
DELIMITER ;
CALL customer_orders('ana@example.com');+----+---------+---------------------+ | id | status | ordered_at | +----+---------+---------------------+ | 1 | shipped | 2026-03-02 10:15:00 | | 4 | shipped | 2026-05-05 12:00:00 | +----+---------+---------------------+
The server never sees DELIMITER: PDO sends statements whole, so PHP (Databases with PDO) and Laravel 2,157 migrations (Migrations and Seeders) omit it, and a one-statement body needs no BEGIN ... END (Stored Functions). Data-access labels such as READS SQL DATA are not checked. SQL SECURITY DEFINER, the default, runs the body with its creator's privileges, so a procedure can be an account's only door into a table (Least-Privilege Accounts). A routine keeps the sql_mode in force when it was created.