乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 数据库基础与安装

    • MySQL 简介
    • MySQL 5.6 到 5.7 到 8.0 关键差异速查
    • 安装 MySQL 5.7
    • 连接与断开服务器
    • 创建数据库
    • 创建数据表
    • 数据库与数据表
    • 加载数据
    • 获取数据库信息
    • 批处理模式
    • SHOW 语句汇总
    • FLUSH 与 RESET 语句
    • my.cnf 核心参数
    • 字符集与排序规则
  • 第2章 SQL基础查询

    • SELECT
    • WHERE
    • ORDER BY
    • LIMIT
    • COUNT
    • 聚合函数
    • 比较运算符
    • 逻辑运算符
    • 算术运算符
    • 模式匹配
    • NULL 值处理
    • UPDATE
    • DELETE
    • REPLACE
    • SELECT INTO
  • 第3章 数据类型与运算符

    • 数值类型
    • 字符串类型
    • 日期时间类型
    • BIT 类型
    • ENUM 类型
    • SET 类型
    • JSON 类型
    • 类型转换
  • 第4章 函数与表达式

    • 字符串函数
    • 数值函数
    • 日期函数
    • 全文检索函数
  • 第5章 高级查询与子查询

    • JOIN
    • 子查询
    • UNION
    • GROUP BY
    • HAVING
    • DISTINCT
  • 第6章 表与索引

    • 数据定义语言
    • 修改表结构
    • 视图
    • 修改视图与检查选项
    • 外键
    • 索引
    • 唯一索引
    • 复合索引
    • 存储引擎对比
    • 分区表
    • 第一范式与第二范式
    • 第三范式与 BC 范式
    • 反范式设计
  • 第7章 存储过程与函数

    • 存储过程
    • 存储函数
    • 变量
    • 流程控制
    • 游标
    • 预处理语句
  • 第8章 事务与锁

    • 事务
    • 事务隔离级别
    • 锁机制
    • MVCC
    • 死锁专题分析
    • LOCK TABLES
    • XA 事务
  • 第9章 用户管理与安全

    • 用户管理
    • 权限管理
    • 角色
    • SQL 注入防范
  • 第10章 性能优化入门

    • 执行计划
    • 索引优化
    • 查询优化
    • 查询优化器提示
    • 慢查询日志
    • InnoDB 深入机制
    • InnoDB 专项优化
    • Performance Schema
    • sys Schema
  • 第11章 复制与高可用

    • 主从复制原理
    • 半同步复制配置
    • binlog 开启与 point-in-time 恢复
    • mysqldump 全库备份
    • mysqldump 单表与条件备份
    • mysqldump 恢复与导入
    • xtrabackup 全量热备
    • xtrabackup 准备与恢复
    • xtrabackup 增量与流式备份
  • 第12章 触发器与事件

    • 触发器
    • 事件调度器
  • 参考

    • MySQL 5.7 专业术语大全
    • MySQL 5.7 关键字与保留字大全

变量

导学

存储过程和函数中需要使用变量来临时存储数据、控制循环、传递结果。MySQL 提供了三种变量:局部变量、用户变量和系统变量。本节讲清它们的区别和使用场景。

定义

变量:在 SQL 执行过程中用于临时存储数据的命名内存位置。MySQL 支持局部变量(过程/函数内)、用户变量(会话级)和系统变量(服务器配置)。

局部变量

在存储过程或函数内部使用,作用域限于 BEGIN...END 块。

完整示例:局部变量声明与赋值

当前数据状态

首先建立示例表并插入数据:

CREATE DATABASE IF NOT EXISTS school;
USE school;

DROP TABLE IF EXISTS employees;
CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    emp_name VARCHAR(20),
    dept VARCHAR(20),
    score DECIMAL(5,2)
);

INSERT INTO employees (emp_name, dept, score) VALUES
('大翔', '技术部', 100),
('白歌', '技术部', NULL);

当前 employees 表数据:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

操作语句

创建使用局部变量的存储过程:

DELIMITER //

CREATE PROCEDURE CalcSum(IN a INT, IN b INT, OUT result INT)
BEGIN
    DECLARE temp INT DEFAULT 0;  -- 声明局部变量,默认值为 0
    SET temp = a + b;
    SET result = temp;
END //

DELIMITER ;

-- 调用
SET @res = 0;
CALL CalcSum(10, 20, @res);
SELECT @res AS result;

操作后的结果

返回:

result
30

结果解读

  • DECLARE temp INT DEFAULT 0; 声明局部变量 temp,仅在 BEGIN...END 块内有效
  • SET temp = a + b; 使用 SET 为局部变量赋值
  • 局部变量不能在该块外部访问,过程执行完毕后自动释放

完整示例:局部变量通过 SELECT INTO 赋值

当前数据状态

基于 employees 表:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

操作语句

创建使用 SELECT ... INTO 的存储过程:

DELIMITER //

CREATE PROCEDURE GetStats()
BEGIN
    DECLARE v_count INT DEFAULT 0;
    DECLARE v_avg DECIMAL(5,2);
    DECLARE v_max DECIMAL(5,2);

    SELECT COUNT(*) INTO v_count FROM employees;
    SELECT AVG(score) INTO v_avg FROM employees;
    SELECT MAX(score) INTO v_max FROM employees;

    SELECT v_count AS 总人数, v_avg AS 平均分, v_max AS 最高分;
END //

DELIMITER ;

-- 调用
CALL GetStats();

操作后的结果

返回:

总人数平均分最高分
2100.0000100

结果解读

  • SELECT COUNT(*) INTO v_count FROM employees; 将查询结果赋值给局部变量
  • INTO 子句必须放在 SELECT 列表的末尾,且查询应只返回一行
  • 局部变量 v_count、v_avg、v_max 仅在过程内部有效

声明与赋值的三种方式

DECLARE var_name datatype [DEFAULT value];   -- 声明
SET var_name = expr;                         -- SET 赋值
SELECT col INTO var_name FROM table;         -- SELECT INTO 赋值

用户变量

以 @ 开头,作用域为当前会话,可在任意 SQL 中使用。

完整示例:用户变量的基本使用

当前数据状态

基于 employees 表:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

操作语句

-- 直接赋值
SET @name = '大翔';
SET @age = 20, @score = 90.5;

SELECT @name, @age, @score;

-- 在查询中赋值
SELECT COUNT(*) INTO @total FROM employees;
SELECT @total;

操作后的结果

第一次查询返回:

@name@age@score
大翔2090.5

第二次查询返回:

@total
2

结果解读

  • 用户变量以 @ 开头,在当前会话内有效
  • 可以一次设置多个变量:SET @a = 1, @b = 2;
  • 用户变量未初始化时值为 NULL,不会报错

完整示例:用户变量跨查询传递

当前数据状态

基于 employees 表:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

操作语句

-- 第一步:获取总数
SELECT COUNT(*) INTO @total FROM employees;
SELECT @total AS 总人数;

-- 第二步:用它做分页计算
SET @page_size = 1;
SET @total_pages = CEIL(@total / @page_size);
SELECT @total_pages AS 总页数;

-- 第三步:计算最后一页的记录数
SET @last_page_size = @total - (@total_pages - 1) * @page_size;
SELECT @last_page_size AS 最后一页记录数;

操作后的结果

第一步返回:

总人数
2

第二步返回:

总页数
2

第三步返回:

最后一页记录数
1

结果解读

  • @total 在第一步查询中被赋值,后续步骤可以继续使用
  • 用户变量的会话级特性使其非常适合跨查询传递中间结果
  • @total_pages 通过 CEIL(@total / @page_size) 计算,2 条记录每页 1 条需要 2 页
  • @last_page_size 计算最后一页的实际记录数:2 - (2-1)*1 = 1

完整示例:避免变量未初始化的陷阱

当前数据状态

基于 employees 表:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

操作语句

-- 先初始化变量
SET @sum = 0;

-- 使用 := 在 SELECT 中累加
SELECT emp_id, score, @sum := @sum + IFNULL(score, 0) AS running_total
FROM employees
ORDER BY emp_id;

-- 查看最终累加值
SELECT @sum AS 总分;

操作后的结果

累加查询返回:

emp_idscorerunning_total
1100100
2NULL100

最终查询返回:

总分
100

结果解读

  • := 是赋值运算符,用于在 SELECT 中赋值
  • = 在 SET 中是赋值,在 SELECT 中是比较
  • 如果不先初始化 @sum,第一次使用时其值为 NULL,NULL + 85 仍然是 NULL

对比错误写法:

-- 不初始化,直接使用
SELECT emp_id, score, @sum2 := @sum2 + score AS running_total
FROM employees
ORDER BY emp_id;

错误结果:

emp_idscorerunning_total
1100NULL
2NULLNULL

因为 @sum2 未初始化,值为 NULL,导致所有累加结果都是 NULL。

系统变量

以 @@ 开头,分为全局(@@global.var)和会话(@@session.var)两个级别。

完整示例:查看和设置系统变量

操作语句

-- 查看系统变量
SELECT @@global.max_connections;
SELECT @@session.sql_mode;
SELECT @@sql_mode;  -- 默认查看会话级

-- 设置会话变量
SET SESSION sql_mode = 'STRICT_TRANS_TABLES';
SELECT @@sql_mode;

-- 设置全局变量(需 SUPER 权限)
-- SET GLOBAL max_connections = 500;

操作后的结果

查看 @@global.max_connections 返回(具体值因配置而异):

@@global.max_connections
151

查看 @@session.sql_mode 返回(具体值因配置而异):

@@session.sql_mode
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,...

设置会话变量后再查看:

@@sql_mode
STRICT_TRANS_TABLES

结果解读

  • @@global.xxx 查看/设置全局变量,影响所有连接
  • @@session.xxx 或 @@xxx 查看/设置会话变量,只影响当前连接
  • 设置全局变量需要 SUPER 权限,且重启后可能丢失(除非写入配置文件)

常用系统变量

变量含义
@@autocommit是否自动提交事务
@@sql_modeSQL 模式(如 ONLY_FULL_GROUP_BY)
@@max_connections最大连接数
@@innodb_buffer_pool_sizeInnoDB 缓冲池大小
@@character_set_database数据库默认字符集

完整示例:局部变量与用户变量的对比

当前数据状态

基于 employees 表:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

操作语句

-- 用户变量在过程外定义
SET @outer_var = '用户变量值';

DELIMITER //

CREATE PROCEDURE VarScopeDemo()
BEGIN
    DECLARE local_var VARCHAR(50);  -- 局部变量
    SET local_var = '局部变量值';

    -- 局部变量和用户变量都可以在过程内使用
    SELECT local_var AS 局部变量, @outer_var AS 用户变量;
END //

DELIMITER ;

CALL VarScopeDemo();

-- 过程外只能访问用户变量
SELECT @outer_var AS 过程外用户变量;

-- 过程外不能访问局部变量,下面这句会报错
-- SELECT local_var;  -- ERROR 1054 (42S22): Unknown column 'local_var' in 'field list'

操作后的结果

过程内查询返回:

局部变量用户变量
局部变量值用户变量值

过程外查询返回:

过程外用户变量
用户变量值

结果解读

  • 局部变量用 DECLARE 声明,作用域在 BEGIN...END 内,过程外不可见
  • 用户变量用 @var 表示,作用域是整个会话,过程内外都可见
  • 过程中应优先使用局部变量,避免命名冲突和意外副作用

常见误区

误区正解
局部变量和用户变量可以混用语法不同(DECLARE vs SET @),作用域不同。过程中优先用局部变量。
= 和 := 没有区别在 SET 中两者都是赋值;在 SELECT 中 = 是比较,:= 是赋值。
用户变量会随连接断开而保留不会。用户变量是会话级的,连接断开即消失。

面试考点

Q:局部变量和用户变量的区别?

局部变量用 DECLARE 声明,作用域在 BEGIN...END 内;用户变量用 @var 表示,作用域是整个会话。过程中应优先使用局部变量。

Q:SELECT @a = 1 和 SELECT @a := 1 的区别?

= 在 SELECT 中是比较运算符,SELECT @a = 1 是比较 @a 是否等于 1,返回布尔结果;:= 是赋值运算符,SELECT @a := 1 将 1 赋给 @a。

Q:如何查看当前 SQL Mode?

SELECT @@sql_mode; 或 SHOW VARIABLES LIKE 'sql_mode';。

小结

  • 局部变量用于过程/函数内部,用 DECLARE 声明
  • 用户变量以 @ 开头,会话级生命周期
  • 系统变量以 @@ 开头,分为全局和会话级别
  • SELECT 中赋值用 :=,避免与比较运算符 = 混淆

下一章引子:有了变量,还需要流程控制语句来实现条件判断和循环——IF、CASE、LOOP、WHILE。

上一页
存储函数
下一页
流程控制