本文档风哥主要介绍MySQL存储过程创建与使用相关知识,包括存储过程的概念、优点、基本语法、创建方法、调用方式、管理操作等内容,风哥教程参考MySQL官方文档Stored Objects章节,适合DBA和开发人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。更多视频教程www.fgedu.net.cn
Part01-基础概念与理论知识
1.1 MySQL存储过程的概念
存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经过编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。存储过程是数据库编程的重要组成部分,可以将复杂的业务逻辑封装在数据库层面。
- 预编译执行:存储过程在创建时已经编译,执行效率高
- 封装性:将复杂的业务逻辑封装在数据库层面
- 可重用性:一次创建,多次调用
- 安全性:可以通过存储过程控制数据访问权限
- 减少网络流量:减少客户端与服务器之间的数据传输
1.2 MySQL存储过程的优点
使用存储过程有以下几个主要优点:
性能优势:存储过程在创建时编译,后续调用直接执行,避免了SQL语句的重复解析和编译过程。
安全性:可以通过存储过程限制用户对基础表的直接访问,只暴露必要的操作接口。
维护性:业务逻辑集中在数据库层面,修改时只需要修改存储过程,不需要修改应用程序。
减少网络开销:复杂的操作只需要发送一个调用语句,而不是多条SQL语句。
1.3 MySQL存储过程基本语法
MySQL存储过程的基本语法结构如下:
BEGIN
— SQL statements
— Logic code
END;
语法说明:
- CREATE PROCEDURE:创建存储过程的关键字
- procedure_name:存储过程名称,建议使用sp_前缀
- IN:输入参数,调用者传入值
- OUT:输出参数,存储过程返回值
- INOUT:既可以输入也可以输出
- BEGIN…END:存储过程体,包含SQL语句
Part02-生产环境规划与建议
2.1 存储过程命名规范
在生产环境中,建议遵循以下命名规范:
— 格式:sp_[模块]_[功能]_[动作]
— 例如:
sp_user_insert — 用户模块插入
sp_user_update — 用户模块更新
sp_user_delete — 用户模块删除
sp_user_get_by_id — 用户模块按ID查询
sp_order_calculate — 订单模块计算
sp_report_daily — 报表模块日报
2.2 存储过程安全考虑
存储过程安全需要考虑以下几点:
- 使用DEFINER和SQL SECURITY控制执行权限
- 避免在存储过程中拼接SQL字符串,防止SQL注入
- 对输入参数进行验证和过滤
- 限制存储过程的访问权限
2.3 存储过程性能优化
存储过程性能优化建议:
- 合理使用索引,确保存储过程中的查询高效
- 避免在循环中执行SQL语句
- 使用事务控制数据一致性
- 定期分析和优化存储过程
Part03-生产环境项目实施方案
3.1 创建存储过程
下面演示如何创建一个简单的存储过程:
mysql> CREATE DATABASE IF NOT EXISTS fgedu_test;
Query OK, 1 row affected (0.01 sec)
mysql> USE fgedu_test;
Database changed
mysql> CREATE TABLE employees (
-> id INT PRIMARY KEY AUTO_INCREMENT,
-> name VARCHAR(50) NOT NULL,
-> department VARCHAR(50),
-> salary DECIMAL(10,2),
-> hire_date DATE
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO employees (name, department, salary, hire_date) VALUES
-> (‘张三’, ‘技术部’, 15000.00, ‘2023-01-15’),
-> (‘李四’, ‘销售部’, 12000.00, ‘2023-03-20’),
-> (‘王五’, ‘技术部’, 18000.00, ‘2022-06-10’),
-> (‘赵六’, ‘人事部’, 10000.00, ‘2023-07-01’);
Query OK, 4 rows affected (0.01 sec)
Records: 4 Duplicates: 0 Warnings: 0
— 创建简单存储过程:查询所有员工
mysql> DELIMITER //
mysql> CREATE PROCEDURE sp_get_all_employees()
-> BEGIN
-> SELECT * FROM employees ORDER BY id;
-> END //
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER ;
3.2 调用存储过程
使用CALL语句调用存储过程:
mysql> CALL sp_get_all_employees();
+—-+——+————+———-+————+
| id | name | department | salary | hire_date |
+—-+——+————+———-+————+
| 1 | 张三 | 技术部 | 15000.00 | 2023-01-15 |
| 2 | 李四 | 销售部 | 12000.00 | 2023-03-20 |
| 3 | 王五 | 技术部 | 18000.00 | 2022-06-10 |
| 4 | 赵六 | 人事部 | 10000.00 | 2023-07-01 |
+—-+——+————+———-+————+
4 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
— 创建带参数的存储过程
mysql> DELIMITER //
mysql> CREATE PROCEDURE sp_get_employee_by_dept(IN dept_name VARCHAR(50))
-> BEGIN
-> SELECT * FROM employees
-> WHERE department = dept_name
-> ORDER BY salary DESC;
-> END //
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER ;
— 调用带参数的存储过程
mysql> CALL sp_get_employee_by_dept(‘技术部’);
+—-+——+————+———-+————+
| id | name | department | salary | hire_date |
+—-+——+————+———-+————+
| 3 | 王五 | 技术部 | 18000.00 | 2022-06-10 |
| 1 | 张三 | 技术部 | 15000.00 | 2023-01-15 |
+—-+——+————+———-+————+
2 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
3.3 管理存储过程
存储过程的管理操作包括查看、修改和删除:
mysql> SHOW PROCEDURE STATUS WHERE Db = ‘fgedu_test’;
+————+————————-+———–+—————-+———————+———————-+—————+———+———————-+———————-+——————–+
| Db | Name | Type | Definer | Modified | Created | Security_type | Comment | character_set_client | collation_connection | Database Collation |
+————+————————-+———–+—————-+———————+———————-+—————+———+———————-+———————-+——————–+
| fgedu_test | sp_get_all_employees | PROCEDURE | root@localhost | 2026-04-01 10:00:00 | 2026-04-01 10:00:00 | DEFINER | | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci |
| fgedu_test | sp_get_employee_by_dept | PROCEDURE | root@localhost | 2026-04-01 10:01:00 | 2026-04-01 10:01:00 | DEFINER | | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci |
+————+————————-+———–+—————-+———————+———————-+—————+———+———————-+———————-+——————–+
2 rows in set (0.00 sec)
— 查看存储过程定义
mysql> SHOW CREATE PROCEDURE sp_get_employee_by_dept\G
*************************** 1. row ***************************
Procedure: sp_get_employee_by_dept
sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_get_employee_by_dept`(IN dept_name VARCHAR(50))
BEGIN
SELECT * FROM employees
WHERE department = dept_name
ORDER BY salary DESC;
END
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
— 删除存储过程
mysql> DROP PROCEDURE IF EXISTS sp_get_all_employees;
Query OK, 0 rows affected (0.01 sec)
Part04-生产案例与实战讲解
4.1 业务报表统计存储过程
创建一个用于统计部门薪资的存储过程:
mysql> DELIMITER //
mysql> CREATE PROCEDURE sp_department_salary_stats(OUT total_count INT, OUT total_salary DECIMAL(15,2))
-> BEGIN
-> — 统计员工总数
-> SELECT COUNT(*) INTO total_count FROM employees;
->
-> — 统计薪资总额
-> SELECT COALESCE(SUM(salary), 0) INTO total_salary FROM employees;
->
-> — 显示各部门统计
-> SELECT
-> department AS 部门,
-> COUNT(*) AS 人数,
-> CONCAT(FORMAT(AVG(salary), 2)) AS 平均薪资,
-> CONCAT(FORMAT(SUM(salary), 2)) AS 薪资总额
-> FROM employees
-> GROUP BY department
-> ORDER BY SUM(salary) DESC;
-> END //
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER ;
— 调用存储过程
mysql> CALL sp_department_salary_stats(@count, @total);
+——–+——+————–+————–+
| 部门 | 人数 | 平均薪资 | 薪资总额 |
+——–+——+————–+————–+
| 技术部 | 2 | 16,500.00 | 33,000.00 |
| 销售部 | 1 | 12,000.00 | 12,000.00 |
| 人事部 | 1 | 10,000.00 | 10,000.00 |
+——–+——+————–+————–+
3 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
— 查看输出参数
mysql> SELECT @count AS 员工总数, @total AS 薪资总额;
+————–+————-+
| 员工总数 | 薪资总额 |
+————–+————-+
| 4 | 55000.00 |
+————–+————-+
1 row in set (0.00 sec)
4.2 数据批量处理存储过程
创建一个批量插入数据的存储过程:
mysql> DELIMITER //
mysql> CREATE PROCEDURE sp_batch_insert_employees(IN num_records INT)
-> BEGIN
-> DECLARE i INT DEFAULT 1;
-> DECLARE dept VARCHAR(50);
->
-> WHILE i <= num_records DO -> — 根据序号分配部门
-> SET dept = CASE
-> WHEN i % 3 = 0 THEN ‘技术部’
-> WHEN i % 3 = 1 THEN ‘销售部’
-> ELSE ‘人事部’
-> END;
->
-> INSERT INTO employees (name, department, salary, hire_date)
-> VALUES (
-> CONCAT(‘员工’, LPAD(i, 3, ‘0’)),
-> dept,
-> 10000 + (i * 100),
-> DATE_ADD(‘2023-01-01’, INTERVAL FLOOR(RAND() * 365) DAY)
-> );
->
-> SET i = i + 1;
-> END WHILE;
->
-> SELECT CONCAT(‘成功插入 ‘, num_records, ‘ 条记录’) AS result;
-> END //
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER ;
— 调用存储过程批量插入10条记录
mysql> CALL sp_batch_insert_employees(10);
+————————-+
| result |
+————————-+
| 成功插入 10 条记录 |
+————————-+
1 row in set (0.02 sec)
Query OK, 0 rows affected (0.02 sec)
— 查看结果
mysql> SELECT COUNT(*) AS total_records FROM employees;
+—————+
| total_records |
+—————+
| 14 |
+—————+
1 row in set (0.00 sec)
4.3 复杂业务逻辑存储过程
创建一个包含事务处理的复杂存储过程:
mysql> DELIMITER //
mysql> CREATE PROCEDURE sp_adjust_salary(
-> IN emp_id INT,
-> IN adjust_rate DECIMAL(5,2),
-> OUT result_msg VARCHAR(100)
-> )
-> BEGIN
-> DECLARE old_salary DECIMAL(10,2);
-> DECLARE new_salary DECIMAL(10,2);
-> DECLARE emp_name VARCHAR(50);
->
-> — 声明异常处理
-> DECLARE EXIT HANDLER FOR SQLEXCEPTION
-> BEGIN
-> ROLLBACK;
-> SET result_msg = ‘操作失败,已回滚’;
-> END;
->
-> START TRANSACTION;
->
-> — 获取当前薪资
-> SELECT name, salary INTO emp_name, old_salary
-> FROM employees WHERE id = emp_id FOR UPDATE;
->
-> IF old_salary IS NULL THEN
-> SET result_msg = CONCAT(‘员工ID ‘, emp_id, ‘ 不存在’);
-> ROLLBACK;
-> ELSE
-> — 计算新薪资
-> SET new_salary = old_salary * (1 + adjust_rate/100);
->
-> — 更新薪资
-> UPDATE employees SET salary = new_salary WHERE id = emp_id;
->
-> SET result_msg = CONCAT(‘员工 ‘, emp_name, ‘ 薪资从 ‘, old_salary,
-> ‘ 调整为 ‘, new_salary, ‘,调整幅度 ‘, adjust_rate, ‘%’);
->
-> COMMIT;
-> END IF;
-> END //
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER ;
— 调用存储过程调整薪资
mysql> CALL sp_adjust_salary(1, 10.00, @msg);
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT @msg AS 调整结果;
+————————————————————–+
| 调整结果 |
+————————————————————–+
| 员工 张三 薪资从 15000.00 调整为 16500.00,调整幅度 10.00% |
+————————————————————–+
1 row in set (0.00 sec)
Part05-风哥经验总结与分享
5.1 存储过程最佳实践
根据多年MySQL数据库管理经验,总结以下存储过程最佳实践:
- 命名规范:使用统一的前缀(如sp_)和清晰的命名规则
- 参数验证:在存储过程开始处验证输入参数的有效性
- 错误处理:使用DECLARE HANDLER处理异常情况
- 事务控制:涉及数据修改的操作使用事务确保一致性
- 注释文档:为存储过程添加清晰的注释说明
5.2 常见问题与解决方案
- 问题1:存储过程编译错误 – 检查语法,特别是DELIMITER的使用
- 问题2:权限不足 – 确保用户有CREATE ROUTINE权限
- 问题3:参数类型不匹配 – 确保调用时参数类型与定义一致
- 问题4:性能问题 – 使用EXPLAIN分析存储过程中的查询
5.3 调试与维护工具
MySQL存储过程调试工具和方法:
mysql> DELIMITER //
mysql> CREATE PROCEDURE sp_debug_example(IN param1 INT)
-> BEGIN
-> DECLARE var1 INT;
-> SET var1 = param1 * 2;
->
-> — 调试输出
-> SELECT param1 AS input_param, var1 AS calculated_value;
->
-> — 实际业务逻辑
-> SELECT * FROM employees WHERE id = var1;
-> END //
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER ;
— 查看存储过程依赖
mysql> SELECT * FROM mysql.proc WHERE db = ‘fgedu_test’ AND type = ‘PROCEDURE’\G
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
