一、MySQL进阶
1. 存储过程
1.1 传统SQL的局限性
在早期的数据库应用开发中,我们面临这样的困境:
sql
-- 一个典型的电商订单处理流程需要执行多个独立操作
START TRANSACTION;
-- 1. 检查库存
SELECT quantity FROM products WHERE id = 1001;
-- 2. 插入订单记录
INSERT INTO orders (user_id, total_amount) VALUES (123, 299.00);
-- 3. 更新库存
UPDATE products SET quantity = quantity - 1 WHERE id = 1001;
-- 4. 记录操作日志
INSERT INTO operation_logs (user_id, action) VALUES (123, 'place_order');
COMMIT;
问题暴露:
-
📍 网络开销大:每次操作都要进行网络往返
-
📍 代码重复:同样的逻辑在不同地方重复编写
-
📍 维护困难:业务逻辑分散在应用代码各处
-
📍 安全性差:SQL注入风险高
1.2 存储过程的诞生
就像从"手工作坊"进化到"自动化工厂",存储过程(Stored Procedure)应运而生,它将一系列SQL语句封装在数据库服务器端,提供了一种更高效、更安全的数据库操作方式。
存储过程是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数来调用执行。
形象的比喻:
| 传统SQL | 存储过程 |
|---|---|
| 手工组装家具(按说明书一步步来) | 买成品家具(一键安装) |
| 每次都要告诉厨师怎么做菜 | 告诉厨师"我要红烧肉" |
| 手动开车(换挡、踩油门) | 自动驾驶 |

sql
应用程序层
↓ 调用(传入参数)
数据库层 → 存储过程执行引擎
↓ 编译优化
↓ 执行SQL语句
↓ 返回结果
应用程序层 ← 接收返回数据

1.3 基本语法框架
在命令行中:
sql
-- 基础语法模板
DELIMITER $$ -- 更改分隔符,避免与过程体中的分号冲突
CREATE PROCEDURE procedure_name([参数列表])
BEGIN
-- 过程体:SQL语句集合
END $$
DELIMITER ; -- 恢复分隔符


sql
-- 1. 创建存储过程
DELIMITER $$
CREATE PROCEDURE ProcessOrder()
BEGIN
-- 过程逻辑
END $$
DELIMITER ;
-- 2. 查看存储过程
SHOW PROCEDURE STATUS WHERE Db = 'your_database';
SHOW CREATE PROCEDURE ProcessOrder;
-- 3. 调用存储过程
CALL ProcessOrder();
-- 4. 修改存储过程(必须先删除再创建)
DROP PROCEDURE IF EXISTS ProcessOrder;
-- 重新CREATE
-- 5. 删除存储过程
DROP PROCEDURE ProcessOrder;
1.4 三种变量的使用艺术
系统变量
系统变量是MySQL服务器维护的配置参数,分为全局 和会话级别。不设置的话默认是会话变量session
全局变量:在所有的会话窗口中都有效!
会话变量:只在创建的会话窗口中有效!

sql
-- 查看所有系统变量
SHOW VARIABLES;
-- 查看特定系统变量
SHOW VARIABLES LIKE 'autocommit';
SHOW VARIABLES LIKE 'wait_timeout';
-- 全局变量(影响所有新连接)
-- 查看全局变量
SELECT @@GLOBAL.autocommit;
SHOW GLOBAL VARIABLES LIKE 'autocommit';
-- 设置全局变量(需要SUPER权限)
SET GLOBAL wait_timeout = 300;
SET @@GLOBAL.wait_timeout = 300;
-- 会话变量(只影响当前连接)
-- 查看会话变量
SELECT @@SESSION.autocommit;
SHOW SESSION VARIABLES LIKE 'autocommit';
-- 设置会话变量
SET SESSION autocommit = 0;
SET @@SESSION.autocommit = 0;
SET autocommit = 0; -- 默认是SESSION
-- 在存储过程中使用系统变量示例
DELIMITER $$
CREATE PROCEDURE CheckConnectionSettings()
BEGIN
DECLARE autoCommitVal BOOLEAN;
DECLARE timeoutVal INT;
-- 获取当前会话的设置
SET autoCommitVal = @@SESSION.autocommit;
SET timeoutVal = @@SESSION.wait_timeout;
SELECT
CONCAT('AutoCommit: ', autoCommitVal) AS Setting1,
CONCAT('Wait Timeout: ', timeoutVal, ' seconds') AS Setting2;
END $$
DELIMITER ;
CALL CheckConnectionSettings();
用户定义变量
用户变量以@开头,在当前会话中有效,可以跨多个SQL语句使用。

sql
-- 定义用户变量(三种方式)
SET @userCount = 0;
SELECT @totalPrice := 100.50; -- 使用 := 赋值
SELECT COUNT(*) INTO @userCount FROM users;
-- 使用用户变量
SELECT @userCount AS TotalUsers;
-- 在多个语句间共享数据
SET @discountRate = 0.1; -- 10%折扣
SELECT
product_name,
price,
price * @discountRate AS discount_amount,
price * (1 - @discountRate) AS final_price
FROM products
WHERE category = 'Electronics';
-- 用户变量在存储过程中的应用
DELIMITER $$
CREATE PROCEDURE CalculateOrderSummary(IN orderId INT)
BEGIN
-- 使用用户变量存储中间结果
SELECT SUM(quantity * price) INTO @subtotal
FROM order_items WHERE order_id = orderId;
-- 计算税费(假设税率8%)
SET @tax = @subtotal * 0.08;
-- 计算总计
SET @total = @subtotal + @tax;
-- 返回所有计算结果
SELECT
@subtotal AS subtotal,
@tax AS tax,
@total AS total_amount;
END $$
DELIMITER ;
CALL CalculateOrderSummary(1001);
注意事项:
-
用户变量在整个会话期间都有效
-
多个存储过程可以共享用户变量
-
用户变量名不区分大小写(MySQL中)
局部变量
局部变量在存储过程内部声明和使用,生命周期仅限于BEGIN-END块。
三种变量的对比分析
| 特性 | 系统变量 | 用户定义变量 | 局部变量 |
|---|---|---|---|
| 前缀 | @@ | @ | 无 |
| 作用域 | 全局/会话 | 当前会话 | BEGIN-END块 |
| 生命周期 | 重启前/会话结束 | 会话结束 | 块结束 |
| 声明方式 | MySQL内置 | SET或SELECT | DECLARE |
| 数据类型 | 系统决定 | 动态类型 | 必须声明类型 |
| 典型用途 | 服务器配置 | 跨语句数据传递 | 过程内部计算 |
1.5 为什么选择存储过程?
性能优势
-
减少网络传输:一次调用代替多次SQL请求
-
预编译优化:SQL语句只需编译一次
-
减少解析时间:避免重复解析相同的SQL
安全优势
-
权限控制:可限制直接表访问,只通过存储过程操作
-
SQL注入防护:参数化调用减少注入风险
-
审计跟踪:所有操作通过存储过程记录日志
维护优势
-
业务逻辑集中:修改只需在数据库端进行
-
版本控制:存储过程可统一管理版本
-
减少客户端代码:简化应用程序开发
何时使用存储过程?
✅ 适合场景:
-
复杂的业务逻辑处理
-
批量数据操作
-
需要事务完整性的操作
-
高频执行的查询逻辑
❌ 不适合场景:
-
简单的CRUD操作
-
业务逻辑变化频繁
-
需要跨数据库移植
