暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Mysql存储过程

爱编码 2021-06-24
307

简介

存储过程就是一条或者多条SQL语句的集合,可以视为批文件。它可以定义批量插入的语句,也可以定义一个接收不同条件的SQL。

存储函数与存储过程本质上是一样的,都是封装一系列SQL语句,简化调用。我们自己编写的存储函数可以像MySQL函数那样自由的被调用。

其实我用到最多的就是如果项目已经部署上线了,但是有些数据是出错了,导致无法正常使用,要修改数据表的方式来解决,但又涉及多个表联动操作的话,这个时候我第一时间就是用存储过程来解决,当然你也可以通过写Java代码来操作。

基础入门

本文基于mysql5.7以上版本

创建存储过程的语句为CREATE PROCEDURE,创建存储函数的语句为CREATE FUNCTION调用存储过程的语句为CALL。调用存储函数的形式就像调用MySQL内部函数一样。

例子:1、准备:创建表并插入数据

  1. DROP TABLE IF EXISTS t_student;


  2. CREATE TABLE t_student

  3. (

  4. id INT(11) PRIMARY KEY AUTO_INCREMENT,

  5. name VARCHAR(255) NOT NULL,

  6. age INT(11) NOT NULL

  7. );


  8. INSERT INTO t_student VALUES(NULL,'aaa',22),(NULL,'bbb',20);

2、创建存储过程:

  1. DROP PROCEDURE IF EXISTS getStuById;


  2. DELIMITER // -- 定义存储过程结束符号为//

  3. CREATE PROCEDURE getStuById(IN stuId INT(11),OUT stuName VARCHAR(255),OUT stuAge INT(11)) -- 定义输入与输出参数

  4. COMMENT 'query students by their id' -- 提示信息

  5. SQL SECURITY DEFINER -- DEFINER指明只有定义此SQL的人才能执行,MySQL默认也是这个

  6. BEGIN

  7. SELECT name ,age INTO stuName , stuAge FROM t_student WHERE id = stuId; -- 分号要加

  8. END // -- 结束符要加

  9. DELIMITER ; -- 重新定义存储过程结束符为分号

语法:

CREATE PROCEDURE sp_name(定义输入输出参数) [ 存储特性 ]  BEGIN SQL语句;  END

IN 表示输入参数,OUT表示输出参数,INOUT表示既可以输入也可以输出的参数。sp_name为存储过程的名字

如果此存储过程没有任何输入输出,其实就没什么意义了,但是sp_name()的括号不能省略。

3、调用存储过程:下面是调用存储过程。对于存储过程提供的临时变量而言,MySQL规定要加上@开头。

  1. #study 是当前数据库名称


  2. CALL study.getStuById(1,@name,@age);


  3. SELECT @name AS stuName,@age AS stuAge;

4、创建存储函数

  1. DROP FUNCTION IF EXISTS getStuNameById;


  2. DELIMITER //

  3. CREATE FUNCTION getStuNameById(stuId INT) -- 默认是IN,但是不能写上去。stuId视为输入的临时变量

  4. RETURNS VARCHAR(255) -- 指明返回值类型

  5. RETURN (SELECT name FROM t_student WHERE id = stuId); // -- 指明SQL语句,并使用结束标记。注意分号位置

  6. DELIMITER ;

5、调用存储函数

  1. SELECT getStuNameById(1);

小结:从上述存储函数的写法上来看,存储函数有一定的缺点。首先与存储过程一样,只能返回一条结果记录。另外就是存储函数只能指明一列数据作为结果,而存储过程能够指明多列数据作为结果。

变量定义

如果希望MySQL执行批量插入的操作,那么至少要有一个计数器来计算当前插入的是第几次。这里的变量是用在存储过程中的SQL语句中的,变量的作用范围在BEGIN .... END 中。没有DEFAULT子句,初始值为NULL。

定义变量的操作:

  1. DECLARE name,address VARCHAR; -- 发现了吗,SQL中一般都喜欢先定义变量再定义类型,与Java是相反的。

  2. DECLARE age INT DEFAULT 20; -- 指定默认值。若没有DEFAULT子句,初始值为NULL

变量赋值:

  1. SET name = 'jay'; -- name变量设置值

例子:

查询出age为mage的记录的数量。

  1. DROP PROCEDURE IF EXISTS contStById;


  2. DELIMITER // -- 定义存储过程结束符号为//

  3. CREATE PROCEDURE contStById(IN mage INT(11),OUT result INT(11)) -- 定义输入变量

  4. BEGIN

  5. DECLARE sCount INT;

  6. SELECT COUNT(*) INTO sCount FROM t_student WHERE age= mage;

  7. SET result = sCount; -- 用变量为输出结果设值

  8. END // -- 结束符要加

  9. DELIMITER ; -- 重新定义存储过程结束符为分号


  10. CALL contStById(1,@result);

  11. SELECT @result;

流程控制

1、IF语句的使用:

  1. DROP PROCEDURE IF EXISTS testIf;

  2. DELIMITER //

  3. CREATE PROCEDURE testIf(OUT result VARCHAR(255))

  4. BEGIN

  5. DECLARE val VARCHAR(255);

  6. SET val = 'a';

  7. IF val IS NULL

  8. THEN SET result = 'IS NULL';

  9. ELSE SET result = 'IS NOT NULL';

  10. END IF;

  11. END //

  12. DELIMITER ;


  13. CALL testIf(@result);

  14. SELECT @result;

2、CASE语句

  1. DROP PROCEDURE IF EXISTS testCase;

  2. DELIMITER //

  3. CREATE PROCEDURE testCase(OUT result VARCHAR(255))

  4. BEGIN

  5. DECLARE val VARCHAR(255);

  6. SET val = 'a';

  7. CASE val IS NULL

  8. WHEN 1 THEN SET result = 'val is true';

  9. WHEN 0 THEN SET result = 'val is false';

  10. ELSE SELECT 'else';

  11. END CASE;

  12. END //

  13. DELIMITER ;


  14. CALL testCase(@result);

  15. SELECT @result;

3、LOOP语句

LOOP用于重复执行SQL。LEAVE 用于退出循环。

下面一个批量插入的例子:

  1. DROP TABLE IF EXISTS t_student;


  2. CREATE TABLE t_student

  3. (

  4. id INT(11) PRIMARY KEY AUTO_INCREMENT,

  5. name VARCHAR(255) NOT NULL,

  6. age INT(11) NOT NULL

  7. );


  8. DROP PROCEDURE IF EXISTS testLoop;

  9. DELIMITER //

  10. CREATE PROCEDURE testLoop(IN columnCount INT(11))

  11. BEGIN

  12. DECLARE id INT DEFAULT 0;

  13. add_loop:LOOP

  14. SET id = id + 1;

  15. IF id>columnCount THEN LEAVE add_loop;

  16. END IF;

  17. INSERT INTO t_student(id,name,age) VALUES(id,'dayu',22);

  18. END LOOP add_loop;

  19. END //

  20. DELIMITER ;


  21. CALL testLoop(15);

4、WHILE语句

  1. DROP PROCEDURE IF EXISTS testWhile;

  2. DELIMITER //


  3. CREATE PROCEDURE testWhile(IN myCount INT(11),OUT result INT(11))

  4. BEGIN

  5. DECLARE i INT DEFAULT 0 ; -- 定义变量

  6. WHILE i < myCount DO -- 符合条件就循环

  7. -- 核心循环SQL;

  8. SET i = i + 1 ; -- 计数器+1

  9. END WHILE; -- 当不满足条件,结束循环 --分号一定要加!

  10. SET result = i; -- 将变量赋值到输出

  11. END //



  12. CALL testWhile(10,@result);

  13. SELECT @result AS 循环次数;

调用过程

1、存储过程必须使用CALL语句来调用。如果要调用其它数据库的存储过程,需要指定数据库名称。例如 CALL dbname.spname

  1. DELIMITER // --存储过程分隔符设定为//

  2. CREATE PROCEDURE CountStu(IN stu_sex CHAR,OUT num INT) --stu_sex表示输入,num表示输出

  3. BEGIN

  4. SELECT COUNT(*) INTO num FROM t_student WHERE sex = stu_sex; --结果存入num,条件用上变量

  5. END // --以指定分隔符结束存储过程

  6. DELIMITER ; --存储过程分隔符设定为;


  7. -- 调用存储过程

  8. CALL CountStu('男',@num);

  9. SELECT @num;


  10. -- 本质为执行下面的SQL


  11. SELECT COUNT(*) AS @num

  12. FROM t_student WHERE sex = '男';

2、存储函数不需要使用CALL关键字。另外,存储函数的参数类型默认为IN输入。

  1. DELIMITER //

  2. CREATE FUNCTION countStu2(stu_sex CHAR) -- 默认是INOUTINOUT不支持。效果是IN,但是不能加上IN

  3. RETURNS INT

  4. RETURN (SELECT COUNT(*) FROM t_student WHERE sex = stu_sex); // -- 不加结束标记,运行失败

  5. DELIMITER ;


  6. -- 调用存储函数

  7. SELECT countStu2('男');

游标

要处理存储过程中的结果集,请使用游标。游标允许您迭代查询返回的一组行,并相应地处理每行。

MySQL游标为只读,不可滚动和敏感。

1、只读:无法通过光标更新基础表中的数据。2、不可滚动:只能按照SELECT语句确定的顺序获取行。不能以相反的顺序获取行。此外,不能跳过行或跳转到结果集中的特定行。3、敏感:有两种游标:敏感游标和不敏感游标。敏感游标指向实际数据,不敏感游标使用数据的临时副本。敏感游标比一个不敏感的游标执行得更快,因为它不需要临时拷贝数据。但是,对其他连接的数据所做的任何更改都将影响由敏感游标使用的数据,因此,如果不更新敏感游标所使用的数据,则更安全。MySQL游标是敏感的。

您可以在存储过程,存储函数和触发器中使用MySQL游标。

  1. DELIMITER $$


  2. USE `chy2019` $$


  3. DROP PROCEDURE IF EXISTS `copy_order_data` $$


  4. CREATE DEFINER = `root` @`%` PROCEDURE `copy_order_data` (IN p_source VARCHAR (100))

  5. BEGIN

  6. -- 需要定义接收游标数据的变量

  7. DECLARE done BOOLEAN DEFAULT 0 ;

  8. -- 自定义变量

  9. DECLARE var_price DOUBLE DEFAULT NULL ;

  10. DECLARE var_pay_time TIMESTAMP DEFAULT NULL ;

  11. DECLARE var_product VARCHAR (100) DEFAULT NULL ;

  12. DECLARE var_source VARCHAR (100) DEFAULT NULL ;

  13. -- 声明游标

  14. DECLARE cur CURSOR FOR

  15. -- 作用于哪个语句

  16. SELECT

  17. price,

  18. pay_time,

  19. product,

  20. source

  21. FROM

  22. cms_aw_order

  23. WHERE source = p_source ;

  24. -- 设置结束标志

  25. -- 这条语句定义了一个 CONTINUE HANDLER,它是在条件出现时被执行的代码。这里,它指出当 SQLSTATE '02000'出现时,SET done=1 SQLSTATE '02000'是一个未找到条件,当REPEAT由于没有更多的行供循环而不能继续时,出现这个条件

  26. DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1 ;

  27. -- 打开游标

  28. OPEN cur ;

  29. -- 使用repeat循环语法

  30. REPEAT

  31. -- 批读取数据到指定变量上

  32. FETCH cur INTO var_price,

  33. var_pay_time,

  34. var_product,

  35. var_source ;

  36. -- 进行逻辑操作

  37. INSERT INTO cms_aw_order_copy (price, pay_time, product, source)

  38. VALUES

  39. (

  40. var_price,

  41. var_pay_time,

  42. var_product,

  43. var_source

  44. ) ;

  45. -- 循环结束条件

  46. UNTIL done

  47. END REPEAT ;

  48. -- 关闭游标

  49. CLOSE cur ;

  50. END $$


  51. DELIMITER ;

参考文章

https://blog.csdn.net/yanluandai1985/article/details/89632265 https://www.cnblogs.com/chywx/p/10397123.html

总结

MySQL的存储过程与存储函数有什么区别?存储函数只能通过return语句返回单个值或者表对象。存储过程不能用return,但是可以使用多个out参数返回多个值。

基本上存储过程的知识就是上面这些了。还有关于定义条件与定义处理程序,这里就不学了。你如果有需要,可以再参考文章中查看。如果对你有帮助可以收藏一下的哦。

推荐阅读

1SpringBoot全局异常处理

2、JVM的垃圾回收

3、Java内存模型(JMM)


文章转载自爱编码,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论