• [数据库]MYSQL之存储过程


    一 存储过程的特点

    MySQL 5.0 版本开始支持存储过程

    1.1 定义

    • 存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
    • 存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
    • 存储过程在思想上很简单,就是数据库 SQL 语言层面的【代码封装与重用】。

    1.2 优点

    • 存储过程【可封装】,并【隐藏复杂的商业逻辑】。
    • 存储过程【可回传值】,并【可接受参数】。
    • 存储过程【无法使用 SELECT 指令来运行】。因为它是子程序,与查看表,数据表或用户定义函数不同。
    • 存储过程【可用在数据检验】,【强制实行商业逻辑】等。
    • 存储过程简单来说,就是为以后的使用而保存的一条或多条MySQL语句的集合。可将其视为批文件,虽然它们的作用不仅限于批处理。

    1.3 缺点

    尽管有这些缺陷,存储过程还是非常有用的,并且应该尽可能地使用。

    • 【移植性:差】。往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。
    • 【编程开发的复杂程度:高】。一般来说,存储过程的编写比基本SQL语句复杂,编写存储过程需要更高的技能,更丰富的经验。
    • 用户可能【没有创建存储过程的安全访问权限】。许多数据库管理员限制存储过程的创建权限,允许用户使用存储过程,但不允许他们创建存储过程。

    1.4 具体业务场景

    迄今为止,使用的大多数SQL语句都是针对一个或多个表的单条语句。并非所有操作都这么简单,经常会有一个完整的操作需要多条语句才能完成。例如,考虑以下的情形。

    • 为了处理订单,需要核对以保证库存中有相应的物品。
    • 如果库存有物品,这些物品需要预定以便不将它们再卖给别的人,并且要减少可用的物品数量以反映正确的库存量。
    • 库存中没有的物品需要订购,这需要与供应商进行某种交互。
    • 关于哪些物品入库(并且可以立即发货)和哪些物品退订,需要通知相应的客户。

    1.5 适用场景:简单、安全、高性能

    • 1通过把处理封装在容易使用的单元中,【简化复杂的操作】。
    • 2由于不要求反复建立一系列处理步骤,这【保证了数据的完整性和一致性】。如果所有开发人员和应用程序都使用同一(试验和测试)存储过程,则所使用的代码都是相同的。这一点的延伸就是【防止错误】。需要执行的步骤越多,出错的可能性就越大。防止错误保证了数据的一致性。
    • 3【简化对变动的管理】。如果表名、列名或业务逻辑(或别的内容)有变化,只需要更改存储过程的代码。使用它的人员甚至不需要知道这些变化。这一点的延伸就是【安全性】。通过存储过程限制对基础数据的访问减少了数据讹误(无意识的或别的原因所导致的数据讹误)的机会。
    • 4【提高性能】。因为使用存储过程比使用单独的SQL语句要快。
    • 5存在一些只能用在单个请求中的MySQL元素和特性,存储过程可以使用它们来【编写功能更强更灵活的代码】。

    二 存储过程的使用 -- 创建与调用

    2.1 无参数的存储过程

    -- DELIMITER $$ # [可选项]声明语句结束符 - 将语句的结束符号从分号;临时改为两个$$(即 可自定义)
    DROP PROCEDURE IF EXISTS procedure_view_all_student;
    CREATE PROCEDURE procedure_view_all_student() -- 查看所有学生信息
    BEGIN -- 1执行段 - 起始 
        DECLARE varName INT DEFAULT 0;  -- 变量 de 声明
        SET varName = 100+1; -- 变量 de 赋值(SET =) 1
        # 变量(varName)
        # 变量的使用: 必须先声明,才能后使用
        # 变量的声明格式: DECLARE variable_name datatype(size) [DEFAULT default_value];
        SELECT COUNT(*) INTO varName FROM Student; -- 变量 de 赋值(SELECT INTO) 2
        SELECT varName AS '总人数'; -- 变量 de 输出
    END; -- 2执行段 - 结束
    -- END $$
    
    CALL procedure_view_all_student;
    

    2.2 仅含输入参数(IN)的存储过程

    DROP PROCEDURE IF EXISTS procedure_view_select_course_of_one_student; -- 查看 指定某一学生的选课信息
    CREATE PROCEDURE procedure_view_select_course_of_one_student(IN sno CHAR(9))
    BEGIN
    	SELECT * FROM select_course AS sc WHERE sc.sno = sno;
    END; 
    
    CALL procedure_view_select_course_of_one_student('201215121');
    

    2.3 仅含输出参数(OUT)的存储过程

    DROP PROCEDURE IF EXISTS procedure_view_total_student; -- 查看 总共的学生数目
    CREATE PROCEDURE procedure_view_total_student(OUT total INT)
    BEGIN
    	SELECT COUNT(*)
    		INTO total
    		FROM select_course AS sc;
    END;
    
    CALL procedure_view_total_student(@total_student);
    SELECT @total_student AS '学生总人数';
    

    2.4 含又作输出又作输入参数(INOUT)的存储过程

    DROP PROCEDURE IF EXISTS procedure_create_huge_test_records_for_student; 
    CREATE PROCEDURE procedure_create_huge_test_records_for_student(INOUT records_num INT) -- 插入 指定数目的学生信息的测试数据
    BEGIN
    	DECLARE sno CHAR(9);
    	DECLARE sname VARCHAR(30);
    	DECLARE ssex VARCHAR(2) DEFAULT '男';
    	DECLARE sage SMALLINT DEFAULT 29;
    	DECLARE sdept VARCHAR(50) DEFAULT 'MS';
    
    	DECLARE counter INT;
    	SET counter = records_num;
    	START TRANSACTION; -- 整个存储过程指定为1个事务
    	-- ↑ 大量插删数据时,追加事务可避免插删每条数据时频繁的磁盘IO交互,便不再受磁盘IO限制,暂存到了内存缓存区,以此大大提高速度
    		WHILE counter >= 1 DO
    			SELECT MID(UUID(), 1, 9) INTO sno; -- 从36位的字符串UUID()中的第1个位置截取9个字符:
    			SELECT MID(UUID(), 1, 5) INTO sname;
    			insert into Student (`sno`, `sname`, `ssex`, `sage`, `sdept`) VALUES(sno, sname, ssex, sage, sdept);
    			SET counter = counter - 1;
    		END WHILE;
    	COMMIT; -- 必须主动提交
    END;
    
    SET @records_num = 200000; #插入20万条数据 总计 36.296秒(启用了事务 start transaction + sql... + commit)
    call procedure_create_huge_test_records_for_student(@records_num);
    SELECT * FROM student WHERE student.sno= '1231546-';
    

    2.5 循环存储过程

    2.5.1 简单示例

    # DEMO - 存储过程 - 循环篇
    -- CALL testloop(@out1); SELECT @out1 as out; --out := 11
    CREATE PROCEDURE testloop(OUT outvalue INT(11))
    BEGIN
    	DECLARE max_value INT(11) DEFAULT 10;
    	DECLARE i INT(11);   -- 局部变量(i , max_value) 必须先声明,后使用
    	SET i = 0;	
    	myLoop:LOOP
    		SET i = i+1;
    		IF i > max_value THEN
    			LEAVE myLoop;
    		END IF;
    	END LOOP myLoop;
    	SET outvalue = i;
    END
    

    2.5.2 自动插入N条记录

    /**
     * 自动插入 `Person`表 amount 条记录
     * @param in  amount : 插入自动测试记录的总数
     * @param out result: 执行结果
     */
    # 1 [调用存储过程]
    -- SET @number = 10; CALL loopInsertRecords(@number, @exe_result); SELECT @exe_result as `执行结果`; 
    -- `执行结果` := [执行结果]成功10; 失败:0 
    # 2 [创建 存储过程]
    DROP PROCEDURE IF EXISTS `loopInsertRecords`;
    CREATE PROCEDURE loopInsertRecords(IN amount INT(10), OUT result VARCHAR(100))
    BEGIN
    	DECLARE i INT(10) DEFAULT 1; -- 局部变量 必须先声明,后使用
    	DECLARE success INT DEFAULT 0;
    	DECLARE fail INT DEFAULT amount;
    	DECLARE hasSqlError INT DEFAULT FALSE; -- 声明1个变量,标识是否有SQL异常
    	DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET hasSqlError=TRUE; -- 在执行过程中出任何SQL异常, 则: 设置hasSqlError为TRUE
    	myLoop:LOOP
    		SET i = i+1;
    		START TRANSACTION; -- transaction start
    			INSERT INTO `CJ_TEST`.`Person` (
    				`CSSJY111`, 
    				`CSSJY107`, 
    				`CSSJY106`, 
    				`CSSJY112`, 
    				`CSSJY103`, 
    				`CSSJY113`, 
    				`CSSJY104`, 
    				`CSSJY108`, 
    				`CSSJY102`, 
    				`CSSJY109`, 
    				`CSSJY101`, 
    				`CSSJY110`, 
    				`CSSJY105`
    			)  VALUES (
    				SUBSTR(REPLACE(UUID(), "-", "@"),1, 3), 
    				1, 
    				2, 
    				DATE_ADD(NOW(), INTERVAL -10 DAY), -- INTERVAL -N DAY: N天前;  INTERVAL N DAY: N天后
    				SUBSTR(REPLACE(UUID(), "-", "@"),1, 10), 
    				"blob-data", 
    				SUBSTR(REPLACE(UUID(), "-", "#"),1, 10), 
    				SUBSTR(REPLACE(UUID(), "-", "$"),1, 10), 
    				SUBSTR(REPLACE(UUID(), "-", "%"),1, 10), 
    				SUBSTR(REPLACE(UUID(), "-", "&"),1, 10), 
    				SUBSTR(REPLACE(UUID(), "-", "*"),1, 10), 
    				DATE_ADD( NOW(), INTERVAL 10 DAY ), -- INTERVAL -N DAY: N天前;  INTERVAL N DAY: N天后 
    				( SELECT ROUND( exp(sum(log(RAND())+log(100))) , 0 ) )
    			);
    		IF hasSqlError THEN  -- 根据hasSqlError判断是否有异常,做回滚和提交操作
    			ROLLBACK;
    		ELSE
    			COMMIT; -- transaction end
    			SET success = success+1;
    		END IF;
    		IF i > amount THEN
    			LEAVE myLoop;
    		END IF;
    	END LOOP myLoop;
    	SET fail = amount - success;
    	SET result = CONCAT("[执行结果]成功", success, "; 失败:", fail);
    END
    

    三 存储过程的使用 -- 查看与删除

    3.1 查看指定表的存储过程

    3.1.1 查看 表内的存储过程 【基本信息】
    -- 语法格式: SHOW {PROCEDURE | FUNCTION} STATUS [LIKE ‘pattern’]
    SHOW PROCEDURE STATUS;
    
    3.1.2 查看 表内的存储过程 【详细信息】
    SHOW CREATE PROCEDURE indexName;
    

    3.2 删除 存储过程

    DROP PROCEDURE IF EXISTS indexName;
    

    4 参考文献

  • 相关阅读:
    UVA 10572 Black & White (状压DP)
    ZOJ 3466 The Hive II (插头DP,变形)
    POJ 3133 Manhattan Wiring (插头DP,轮廓线,经典)
    HDU 3377 Plan (插头DP,变形)
    HDU 1964 Pipes (插头DP,变形)
    FZU 1977 Pandora adventure (插头DP,常规)
    URAL 1519 Formula 1 (插头DP,常规)
    POJ 1739 Tony's Tour (插头DP,轮廓线DP)
    HDU 1693 Eat the Trees (插头DP)
    hihoCoder #1162 : 骨牌覆盖问题·三 (矩阵快速幂,DP)
  • 原文地址:https://www.cnblogs.com/johnnyzen/p/12535274.html
Copyright © 2020-2023  润新知