MySQL Procedure Parameters
Master MySQL stored procedure parameters — the three modes IN, OUT, and INOUT with worked examples on the employees table. Covers parameter syntax, why OUT/INOUT need user variables at the call site, the absence of default parameter values in MySQL, and how to choose the right mode for each scenario.
Parameters make stored procedures reusable. Instead of hard-coding values, you accept them as inputs, optionally write outputs, and let callers vary the behavior. MySQL parameters work very differently from regular function arguments in most languages because of parameter modes — three of them: IN, OUT, and INOUT.
Three pieces:
- Mode —
IN(default if omitted),OUT, orINOUT. - Name — follows the same rules as column names.
- Data type — any MySQL type:
INT,VARCHAR(50),DECIMAL(10,2),DATE, etc.
Multiple parameters are separated by commas inside the parentheses after the procedure name.
| Mode | Direction | Description |
|---|---|---|
IN (default) | Caller → procedure | The procedure receives a copy of the value. Modifying it inside the body does not affect the caller. Most common mode. |
OUT | Procedure → caller | The procedure writes a result into this parameter. The caller reads it after the call. The procedure cannot read the parameter's initial value — it starts as NULL inside. |
INOUT | Both | The procedure reads the caller's value and writes back a new one. Best for transform-in-place semantics. |
CREATE PROCEDURE p(IN x INT DEFAULT 10). Every caller must supply every argument explicitly. If you need optional parameters, the workaround is to accept NULL and treat it as "use the default" inside the body.@x kind) at the call site. You can't pass a literal or a local procedure variable for them — the caller needs a "place" to receive the result, and only user variables provide that.The examples reuse the employees table. Set it up once if needed:
DROP TABLE IF EXISTS employees;
CREATE TABLE employees (
employee_id INT UNSIGNED PRIMARY KEY,
first_name VARCHAR(20),
last_name VARCHAR(25) NOT NULL,
salary DECIMAL(10, 2) NOT NULL,
department_id INT
);
INSERT INTO employees VALUES
(100, 'Steven', 'King', 24000.00, 9),
(101, 'Neena', 'Kochhar', 17000.00, 9),
(102, 'Lex', 'De Haan', 17000.00, 9),
(103, 'Alexander', 'Hunold', 9000.00, 6),
(104, 'Bruce', 'Ernst', 6000.00, 6),
(105, 'David', 'Austin', 4800.00, 6),
(106, 'Valli', 'Pataballa', 4800.00, 6),
(107, 'Diana', 'Lorentz', 4200.00, 6),
(108, 'Nancy', 'Greenberg', 12000.00, 10),
(109, 'Daniel', 'Faviet', 9000.00, 10);
Find all employees with a specific salary. The salary value comes in via an IN parameter:
DROP PROCEDURE IF EXISTS emp1;
DELIMITER $$
CREATE PROCEDURE emp1(IN sal DECIMAL(10, 2))
READS SQL DATA
BEGIN
SELECT employee_id, first_name, last_name, salary, department_id
FROM employees
WHERE salary = sal;
END$$
DELIMITER ;
CALL emp1(17000);
Two employees earn exactly 17,000 — Neena and Lex. The IN parameter sal received the value 17000 from the caller.
sal INT while the column was NUMERIC(8,2). Mismatched types work because of implicit conversion, but you risk subtle bugs (rounding, sign errors). Match types deliberately — DECIMAL(10,2) here matches the salary column.Count employees at a given salary, return the count via an OUT parameter:
DROP PROCEDURE IF EXISTS emp2;
DELIMITER $$
CREATE PROCEDURE emp2(IN sal DECIMAL(10, 2),
OUT total INT)
READS SQL DATA
BEGIN
SELECT COUNT(*)
INTO total
FROM employees
WHERE salary = sal;
END$$
DELIMITER ;
-- Call: provide a literal for IN, a user variable for OUT
CALL emp2(17000, @total);
SELECT @total;
The procedure wrote 2 into total, which the caller reads via @total. Notice that the CALL itself doesn't return a result set — the answer is in the user variable.
SELECT total; in the procedure body and you get a result set automatically. If you want the value programmatically available to the caller for further work, an OUT parameter is cleaner — your application's CALL binds to a host variable, no result-set parsing needed.Take a salary value, increase it by 10%, write the result back through the same INOUT parameter, and update the database:
DROP PROCEDURE IF EXISTS emp3;
DELIMITER $$
CREATE PROCEDURE emp3(IN emp_id INT,
INOUT salary DECIMAL(10, 2))
MODIFIES SQL DATA
BEGIN
-- Apply the 10% raise to the parameter directly
SET salary = salary * 1.10;
-- Persist the new salary
UPDATE employees
SET salary = salary -- NB: refers to the parameter
WHERE employee_id = emp_id;
END$$
DELIMITER ;
-- Caller hands in the current salary, gets back the new one
SET @salary = 24000;
CALL emp3(100, @salary);
SELECT @salary;
Started at 24,000, multiplied by 1.10 → 26,400. The new value flowed back to the caller through the INOUT parameter, and Steven King's row in the database was updated to match.
- It declared a local variable
emp_sal, populated it withSELECT salary INTO emp_sal, then never used it — dead code. - The CALL statements were missing semicolons (
SET @salary=24000with no;, etc.) — would fail to parse in themysqlCLI.
INOUT parameter directly and uses proper statement terminators.| If you want to… | Use this mode |
|---|---|
| Pass a value into the procedure for it to read | IN (the default) |
| Get a single value out — a count, a status, an ID | OUT |
| Take a value, transform it, send it back | INOUT |
| Return many rows | None — just SELECT in the body, returns a result set |
| Return multiple separate values | Multiple OUT parameters, or use SELECT a, b, c as a single-row result set |
- Default to
INfor read-only inputs. Most parameters fall into this category. - Match parameter types to column types — same data type and length. Avoids implicit conversion bugs.
- Don't shadow column names with parameter names when you can help it.
WHERE salary = salis fine;WHERE salary = salarywith a parameter also namedsalaryis ambiguous and resolves to "where the column equals itself" — i.e. always true. Prefix parameters (p_salary,i_salary) or keep names distinct. - OUT and INOUT need user variables at the call site. You'll have to declare and pass them; the caller can't supply literals.
- For multiple return values, prefer a single result-set
SELECT a, b, cover many OUT parameters — easier to consume from application code. - Document parameters with
COMMENTwhen their meaning isn't obvious.amountis fine;p_xis not. - Validate inputs early. A procedure that does
UPDATE accounts SET balance = balance - amount WHERE id = senderwith no check foramount > 0is one bug away from a disaster.
- Stored procedure parameters have three modes:
IN(caller → procedure, default),OUT(procedure → caller),INOUT(both directions). OUTandINOUTparameters need user variables at the call site so the caller has somewhere to read the result.OUTparameters start as NULL inside the procedure — the procedure can't read the caller's initial value through them.- MySQL has no default parameter values — every argument must be supplied at every call. Workaround: accept NULL and check inside the body.
- For returning many rows use a plain
SELECTin the body (result set); for a single value use anOUTparameter; for transform-in-place semantics useINOUT. - Match parameter types to column types, prefix parameters to avoid name shadowing, and validate inputs early.