首页 > 数据库 >MySQL存储例程详解:过程与函数

MySQL存储例程详解:过程与函数

来源:互联网 2026-07-10 08:34:12

MySQL从5.0版本开始引入存储过程和函数,将复杂SQL逻辑打包成可复用单元,提高重用性、减少网络传输并增强安全性。函数有返回值,存储过程无返回值。参数类型包括IN、OUT和INOUT,通过CALL语句调用。

MySQL从5.0版本开始引入了存储过程和函数,这一特性在数据库领域具有重要的进步意义。

MySQL存储例程详解:过程与函数

长期稳定更新的攒劲资源: >>>点此立即查看<<<

存储过程和函数的核心作用是将复杂的SQL逻辑封装成一个可复用的单元。应用程序无需关注底层的SQL细节,只需像调用普通函数或执行命令一样调用它即可。

二者基本区别在于:函数必须有返回值,而存储过程没有。当然,这是最直观的区分,后续会展开更多细节。

1. 存储过程概述

1.1 理解

含义:存储过程是一组预先编译好的SQL语句,打包存储在服务器上。英文名为Stored Procedure,直译为“存好的过程”。

执行过程:存储过程预先存储在MySQL服务器中,客户端只需发出调用命令,服务器便会执行预先存储的全部SQL语句。

好处:存储过程具有以下优势:

  • 简化操作:提高SQL语句重用性,减轻开发人员工作压力。
  • 减少失误:代码只需编写一次,避免反复拼接SQL,降低出错概率。
  • 减少网络传输:客户端只需发送调用命令,无需传输大量SQL语句。
  • 提高安全性:业务逻辑隐藏在存储过程内部,SQL语句不暴露在网络中,提升数据查询安全性。

与视图、函数的对比

  • 相同点:都能通过封装使开发更清晰、更安全,并减少网络传输。
  • 不同点:视图本质上是虚拟表,通常不直接操作底层数据表;而存储过程是程序化的SQL,可直接操作底层数据表,能实现复杂处理逻辑。
  • 存储过程与函数:最直观的区别是函数必须有返回值,存储过程没有。

1.2 分类

存储过程的参数类型有三种:IN、OUT和INOUT。根据参数类型可分为以下几类:

  • 没有参数:无参数无返回
  • 仅带IN类型:有参数无返回
  • 仅带OUT类型:无参数有返回
  • 既带IN又带OUT:有参数有返回
  • 带INOUT:有参数有返回(参数既是输入也是输出)

注意:IN、OUT、INOUT可同时出现在一个存储过程中,且可带多个。

2. 创建存储过程

2.1 语法分析

语法格式如下:

CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...]
BEGIN
    存储过程体
END

# 调用
CALL 存储过程名();

编写存储过程时需注意以下几点:

  1. 参数前面的符号
    • IN:输入参数,存储过程只读取该参数的值。未定义参数种类时默认为IN。
    • OUT:输出参数,存储过程执行完成后,调用方可读取参数中返回的值。
    • INOUT:既是输入又是输出,调用时携带值,执行后可能改变。
  2. 形参类型可以是MySQL数据库中的任意类型。
  3. characteristics是存储过程的约束条件,主要包括:
    • LANGUAGE SQL:说明存储过程体由SQL语句组成,当前系统仅支持SQL。
    • [NOT] DETERMINISTIC:指明结果是否确定。确定时相同输入得到相同输出;不确定时相同输入可能得到不同输出;默认不确定。
    • {CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA}:说明子程序使用SQL语句的限制,默认是CONTAINS SQL。
    • SQL SECURITY {DEFINER | INVOKER}:执行权限。DEFINER表示仅创建者可执行,INVOKER表示有访问权限的用户均可执行,默认DEFINER。
    • COMMENT 'string':注释信息,用于描述存储过程。
  4. 存储过程体可包含多个SQL语句,若仅有一条语句,可省略BEGIN和END。
  5. 需设置新的结束标记:MySQL默认以分号结束语句,但存储过程体内也包含分号,会产生冲突。因此需用DELIMITER修改结束符,编写完成后再恢复。例如:
    • DELIMITER //将结束符改为//。
    • 存储过程定义结束后用DELIMITER ;恢复默认结束符。
    • 注意:使用Navicat等工具时会自动处理,无需手动设置。

2.2 代码举例

2.2.1 无参数(无参无返回)

存储过程体为普通查询语句,使用CALL调用。例如:

DELIMITER $
CREATE PROCEDURE select_all_data()
BEGIN
    SELECT * FROM emps;
END $
DELIMITER ;

# 调用
CALL select_all_data();

2.2.2 带OUT(无参有返回)

SELECT结果通过INTO赋值给变量。调用时传入@变量名,再通过SELECT查看变量值。例如:

DELIMITER //
CREATE PROCEDURE show_min_salary(OUT ms DOUBLE)
BEGIN
    SELECT MIN(salary) INTO ms FROM emps;
END //
DELIMITER ;

# 调用
CALL show_min_salary(@ms);
SELECT @ms;

2.2.3 带IN(有参无返回)

传入参数作为筛选条件。调用时可直接传值,也可传入@变量名。例如:

DELIMITER //
CREATE PROCEDURE show_someone_salary(IN empname VARCHAR(20))
BEGIN
    SELECT salary FROM emps WHERE ename = empname;
END //
DELIMITER ;

# 调用方式1
CALL show_someone_salary('Abel');

# 调用方式2
SET @empname = 'Abel';
CALL show_someone_salary(@empname);

2.2.4 带IN和OUT(有参有返回)

组合IN和OUT——输入员工姓名,输出员工薪资。例如:

DELIMITER //
CREATE PROCEDURE show_someone_salary2(IN empname VARCHAR(20), OUT empsalary DOUBLE)
BEGIN
    SELECT salary INTO empsalary FROM emps WHERE ename = empname;
END //
DELIMITER ;

2.2.5 带INOUT(有参数又返回)

参数既是输入也是输出,执行后参数值会被修改。例如:

DELIMITER //
CREATE PROCEDURE show_mgr_name(INOUT empname VARCHAR(20))
BEGIN
    SELECT ename INTO empname FROM emps WHERE eid = (SELECT MID FROM emps WHERE ename=empname);
END //
DELIMITER ;

3. 调用存储过程

3.1 调用格式

存储过程必须使用CALL语句调用。要调用其他数据库的存储过程,需指定数据库名称,如CALL dbname.procname

不同参数模式的调用方式如下:

  • in模式的参数CALL sp1('值');
  • out模式的参数SET @name; CALL sp1(@name); SELECT @name;
  • inout模式的参数SET @name=值; CALL sp1(@name); SELECT @name;

3.2 代码举例

实用示例:实现累加运算,计算1+2+...+n的结果。

DELIMITER //
CREATE PROCEDURE `add_num`(IN n INT)
BEGIN
    DECLARE i INT;
    DECLARE sum INT;
    SET i = 1;
    SET sum = 0;
    WHILE i <= n DO
        SET sum = sum + i;
        SET i = i + 1;
    END WHILE;
    SELECT sum;
END //
DELIMITER ;

CALL add_num(50);

3.3 如何调试

MySQL存储过程缺乏类似Java或C++的专用IDE调试工具。常用方法是print调试法——使用SELECT语句输出中间结果,验证SQL正确性。调试成功后将SELECT语句后移,继续调试后续语句。也可将存储过程中的SQL复制出来逐段单独调试。

值得注意的是,调试过程较为繁琐,意味着维护成本较高。因此在实际生产环境中,通常不推荐大量使用存储过程

4. 存储函数的使用

MySQL支持自定义函数,定义后调用方式与内置函数类似。

4.1 语法分析

CREATE FUNCTION 函数名(参数名 参数类型,...)
RETURNS 返回值类型
[characteristics ...]
BEGIN
    函数体  # 函数体中必须包含 RETURN 语句
END

说明:

  • 函数参数默认为IN,不能指定为OUT或INOUT。
  • RETURNS type必须指定返回类型,函数体内必须包含RETURN value语句。
  • characteristics与存储过程类似。
  • 函数体可使用BEGIN...END,仅一条语句时可省略。

4.2 调用存储函数

调用方式与系统函数相同:SELECT 函数名(实参列表)

4.3 代码举例

例如创建一个函数,查询某人的email:

DELIMITER //
CREATE FUNCTION email_by_name() RETURNS VARCHAR(25)
DETERMINISTIC CONTAINS SQL
BEGIN
    RETURN (SELECT email FROM employees WHERE last_name = 'Abel');
END //
DELIMITER ;

SELECT email_by_name();

注意事项

  • 声明返回值类型时使用RETURNS(带S),但函数体内的RETURN不带S。
  • RETURN语句中不能出现分号。
  • 函数参数前无需写IN、OUT等关键字。
  • 若创建时报错“you might want to use the less safe log_bin_trust_function_creators variable”,有两种处理方式:
    1. 添加必要的函数特性,如DETERMINISTICCONTAINS SQL
    2. 执行SET GLOBAL log_bin_trust_function_creators = 1;

4.4 对比存储函数和存储过程

关键字 调用语法 返回值 应用场景
存储过程 PROCEDURE CALL 存储过程() 理解为有0个或多个 一般用于更新
存储函数 FUNCTION SELECT 函数() 只能是一个 一般用于查询结果为一个值并返回时

存储过程没有传统意义上的返回值,而是通过OUT参数“修改”变量的值。此外,存储函数可放在查询语句中使用,存储过程不行。反之,存储过程功能更强大,能执行创建表、删除表、事务操作等——这些存储函数无法实现。

5. 存储过程和函数的查看、修改、删除

5.1 查看

创建完成后,可通过以下方式查看已编写的存储过程:

  • SHOW CREATE:查看创建信息,如SHOW CREATE FUNCTION test_db.CountProc \G
  • SHOW STATUS:查看状态信息,如SHOW PROCEDURE STATUS LIKE 'pattern',可列出所有存储过程或函数的信息。
  • information_schema.Routines表:直接查询系统表,SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME='存储过程或函数的名'。若存储过程和函数名称相同,需添加AND ROUTINE_TYPE = 'PROCEDURE''FUNCTION'加以区分。

5.2 修改

修改存储过程或函数并非修改功能和逻辑,而是修改相关特性。语法如下:

ALTER {PROCEDURE | FUNCTION} 存储过程或函数的名 [characteristic ...]

可修改的特性包括:

  • CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA
  • SQL SECURITY {DEFINER | INVOKER}
  • COMMENT 'string'

例如:

ALTER PROCEDURE CountProc MODIFIES SQL DATA SQL SECURITY INVOKER ;

5.3 删除

删除存储过程或函数使用DROP语句:

DROP {PROCEDURE | FUNCTION} [IF EXISTS] 存储过程或函数的名

IF EXISTS可防止因对象不存在而报错。

6. 关于存储过程使用的争议

存储过程是否应该使用?业界存在不同观点。部分公司强制要求大型项目使用存储过程,而另一些公司则在开发手册中明确禁止。为何存在如此大的差异?

6.1 优点

  • 一次编译多次使用:存储过程仅在创建时编译,后续调用无需重新编译,执行效率更高。
  • 减少开发工作量:将代码封装成模块,复杂问题拆分为小模块,模块间可复用,代码结构更清晰。
  • 安全性强:可设置使用权限,像视图一样控制特定用户才能执行。
  • 减少网络传输量:每次使用只需调用存储过程,无需传输大量SQL语句。
  • 良好的封装性:原本需要多次连接数据库才能完成的操作,一次调用即可完成。

6.2 缺点

尽管有上述优点,但也存在替代方案。例如阿里巴巴的开发规范中明确规定:

【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。

具体缺点包括:

  • 可移植性差:MySQL、Oracle、SQL Server的存储过程不能通用,更换数据库需重写。
  • 调试困难:仅少数DBMS支持存储过程调试。复杂存储过程的开发和维护不易,虽有第三方工具但通常需付费。
  • 版本管理困难:数据表索引变更可能导致存储过程失效。软件版本迭代时,存储过程缺少版本控制,管理不便。
  • 不适合高并发场景:高并发时数据库压力是瓶颈,分库分表及高可扩展性要求下,存储过程反而成为负担。

小结:存储过程是一把双刃剑——使用方便,但也有局限。不同公司态度各异,但作为开发人员,掌握存储过程仍是必备技能之一。理解其工作原理及解决范围,是基本功。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备2026025700号-3 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。