MySQL Variables MySQL · Stored Procedure · Variables

MySQL Variables

Master MySQL's three flavors of variables — local variables (DECLARE in BEGIN...END), user variables (@x, session-scoped), and system variables (@@x, server configuration). Covers SET vs SELECT INTO assignment, the := operator, SHOW VARIABLES, SET PERSIST in MySQL 8.0, and best practices for choosing the right kind for each scenario.

MySQL has three different kinds of variables, each with its own scope, syntax, and lifetime. They're easy to confuse — and mixing them up causes some of the most common stored-procedure bugs.

KindPrefixScopeLifetime
Local variableNoneInside the BEGIN ... END block where it's declaredLives until the block ends
User variable@The current connection (session)Lives until the connection closes
System variable@@Server-wide (GLOBAL) or per-session (SESSION)Persists with the server (GLOBAL) or session (SESSION)

Local variables are declared inside BEGIN ... END blocks (typically inside stored procedures, functions, or triggers). They have:

  • No @ prefix — just the name.
  • A required data type — strongly typed, you must declare it.
  • Block scope — visible only inside the BEGIN ... END they were declared in.
  • An optional default via DEFAULT; otherwise initialized to NULL.
DECLARE var_name [, var_name ...] datatype[(size)] [DEFAULT default_value];
⚠️ Two declaration rules to remember:
  • All DECLAREs come first in a BEGIN ... END block — before any other statement. The order is variables → cursors → handlers.
  • One DECLARE statement = one data type. You can declare multiple variables in one statement only if they share a type: DECLARE a, b, c INT DEFAULT 0; works; DECLARE a INT, b VARCHAR(10); does not.
Example 1 — Local Variables in a Procedure

Declare a few salaries, sum them, return the result. The original tutorial had several typos in this procedure — missing semicolons, missing space between DECLARE and Lex, inconsistent variable casing — corrected below:

MySQL — Local variables
DROP PROCEDURE IF EXISTS salaries;

DELIMITER $$

CREATE PROCEDURE salaries()
BEGIN
    DECLARE steven INT;
    DECLARE neena  INT DEFAULT 18000;
    DECLARE lex    INT;
    DECLARE bruce  INT;
    DECLARE total  INT;

    SET steven = 25000;
    SET lex    = 20000;
    SET bruce  = 10000;
    SET total  = steven + neena + lex + bruce;

    SELECT total, steven, neena, lex, bruce;
END$$

DELIMITER ;

CALL salaries();
OUTPUT
mysql — salaries
mysql>CALL salaries();
+-------+--------+-------+-------+-------+
| total | steven | neena | lex | bruce |
+-------+--------+-------+-------+-------+
| 73000 | 25000 | 18000 | 20000 | 10000 |
+-------+--------+-------+-------+-------+
1 row in set (0.00 sec)
💡 The two ways to assign values to local variables:
  • SET var = expression; — assign a single value
  • SELECT col INTO var FROM table WHERE ...; — pull from a query (must return exactly one row)

User variables — the @ kind — are session-scoped. They live for the duration of your connection and are visible everywhere in that session: from the CLI, from procedures called by your session, from any query you run.

Unlike local variables, they:

  • Don't need declaration — first assignment creates them.
  • Are loosely typed — type is inferred from the value.
  • Persist across statements — set in one query, read in the next.
  • Are case-insensitive@name and @NAME are the same variable.
SET @var_name = value; -- or SELECT @var_name := value;
Example 2 — Setting and Reading a User Variable
MySQL — User variable basics
SET @name = 'Gowtham';
SELECT @name;
OUTPUT
mysql — user variable
mysql>SET @name = 'Gowtham';
Query OK, 0 rows affected (0.00 sec)
mysql>SELECT @name;
+---------+
| @name |
+---------+
| Gowtham |
+---------+
1 row in set (0.00 sec)
Example 3 — Assignment with SELECT (the := operator)

SELECT @var := value assigns and returns the value in one shot. Useful when you want to capture a query result in a variable while also displaying it:

MySQL — SELECT INTO via :=
SELECT @max_salary := MAX(salary) FROM employees;

-- Now use it
SELECT * FROM employees WHERE salary = @max_salary;
OUTPUT
mysql — max_salary
mysql>SELECT @max_salary := MAX(salary) FROM employees;
+----------------------------+
| @max_salary := MAX(salary) |
+----------------------------+
| 24000.00 |
+----------------------------+
1 row in set (0.00 sec)
mysql>SELECT * FROM employees WHERE salary = @max_salary;
+-------------+------------+-----------+----------+---------------+
| employee_id | first_name | last_name | salary | department_id |
+-------------+------------+-----------+----------+---------------+
| 100 | Steven | King | 24000.00 | 9 |
+-------------+------------+-----------+----------+---------------+
1 row in set (0.00 sec)
⚠️ := in SELECT, = in SET. Inside SET, both = and := work for assignment. Inside SELECT, only := assigns — plain = is comparison. The two-character form is unambiguous and works everywhere; some teams use it always for consistency.
💡 User variables are the bridge to OUT/INOUT parameters. When you call a procedure with CALL proc(@x, @y), those @ variables hold the OUT/INOUT results — they're how the procedure communicates back to your session.

System variables — the @@ kind — are MySQL's configuration knobs. They control server behavior: connection limits, character sets, buffer sizes, transaction isolation, autocommit mode, and hundreds more.

Two scopes:

ScopeAffectsPersists?
GLOBAL (@@GLOBAL.x)Server-wide for all sessionsUntil server restart (some can be made permanent via SET PERSIST)
SESSION (@@SESSION.x or @@x)Just your current connectionUntil you disconnect
Example 4 — List All System Variables

The SHOW VARIABLES command lists system variables. Filter with LIKE to find a specific one:

MySQL — SHOW VARIABLES
SHOW VARIABLES LIKE '%table%';
OUTPUT
mysql — show variables
mysql>SHOW VARIABLES LIKE '%table%';
+----------------------------------+----------------------+
| Variable_name | Value |
+----------------------------------+----------------------+
| big_tables | OFF |
| default_table_encryption | OFF |
| max_heap_table_size | 16777216 |
| table_definition_cache | 2000 |
| table_open_cache | 4000 |
| table_open_cache_instances | 16 |
| tmp_table_size | 16777216 |
+----------------------------------+----------------------+
7 rows in set (0.01 sec)
Example 5 — Read a System Variable

Use SELECT @@var_name to read a single system variable's current value:

MySQL — Read system variable
SELECT @@version, @@autocommit, @@max_connections;
OUTPUT
mysql — system variables
mysql>SELECT @@version, @@autocommit, @@max_connections;
+-----------+--------------+-------------------+
| @@version | @@autocommit | @@max_connections |
+-----------+--------------+-------------------+
| 8.0.36 | 1 | 151 |
+-----------+--------------+-------------------+
1 row in set (0.00 sec)
Example 6 — Change a System Variable

Many system variables are dynamic — you can change them at runtime with SET:

MySQL — Set system variable
-- Just for this session
SET SESSION sql_mode = 'STRICT_ALL_TABLES';

-- Server-wide (requires SUPER privilege)
SET GLOBAL max_connections = 200;

-- Persistent across server restarts (MySQL 8.0+)
SET PERSIST max_connections = 200;
⚠️ Some system variables are read-only. Things like @@version, @@datadir, @@hostname reflect server state that you can't change at runtime — attempting to SET them gives an error. Check the docs for which variables are dynamic (changeable) vs static (read-only).
You want to…Use this
Hold a temporary value inside a procedureLocal variable: DECLARE x INT;
Pass a value between statements in your sessionUser variable: SET @x = ...;
Receive an OUT or INOUT parameter from a procedureUser variable: CALL proc(..., @result);
Read MySQL server configurationSystem variable: SELECT @@var_name;
Tweak server behavior at runtimeSystem variable: SET SESSION/GLOBAL var = ...;
  1. Use local variables inside procedures by default. They're typed, scoped, and predictable.
  2. Watch for variable / column name shadowing. If a local variable shares its name with a table column, MySQL uses the variable in expressions but the column in WHERE clauses — easy bug to introduce. Prefix locals (v_total) or pick distinct names.
  3. Reach for user variables when you need session-scoped state — running totals across queries, OUT parameter receivers, parameterized scripts.
  4. Don't overuse user variables. They're not type-safe and they leak across scope boundaries; relying on them for procedural state is fragile.
  5. Treat system variables as configuration. Read them freely, but be deliberate about SET GLOBAL / SET PERSIST — those changes affect the whole server.
  6. Initialize local variables explicitly. DECLARE total INT DEFAULT 0; is clearer than DECLARE total INT; (which starts as NULL — and NULL + anything is still NULL).
  • MySQL has three kinds of variables: local (no prefix, declared in BEGIN ... END), user (@ prefix, session-scoped), system (@@ prefix, server configuration).
  • Local variables need DECLARE var [, var ...] datatype [DEFAULT value]; at the top of their block. All DECLAREs come before any other statement.
  • User variables are loosely typed, case-insensitive, persist for the connection, and don't need declaration. Set with SET @x = ...; or SELECT @x := ...;.
  • System variables are MySQL's configuration. Read with SELECT @@var or SHOW VARIABLES LIKE '...';; change with SET SESSION, SET GLOBAL, or SET PERSIST (8.0+).
  • Use the right tool: locals for procedure-internal state, user variables for cross-statement values and OUT-parameter receivers, system variables for MySQL configuration.