• MySQL存储过程与游标


      1、存储过程简介

      (1)存储过程:是为以后使用而保存的一条或多条SQL语句或函数。可以将它视为批文件,不过它的作用不仅仅限于批处理;通常被称为函数或子程序。

      (2)支持存储过程的DBMS:Oracle、SQL Server、MySQL 5及更高版本;而Microsoft Access和SQLite不支持存储过程。

      (3)存储过程的优点:简单、安全、高性能

    • 把处理封装在一个易用的单元中,简化了复杂的操作,实现过程化编程
    • 不用反复建立一系列处理步骤,保证数据的一致性,防止错误
    • 简化对变动的管理,以达到安全性;通过存储过程限制对基础数据的访问,减少了数据讹误的机会
    • 存储过程通常以编译过的形式存储,所以DBMS处理命令所需的工作量少,提高了性能;也即是说存储过程的语句已经保存在数据库里了,语句已经被解析过了,以可执行格式存在。
    • 存储过程可以利用SQL元素和特性来编写功能更强大更灵活的代码

      (4)存储过程的缺陷:

    • 不同DBMS中的存储过程语法有所不同,编写真正的可移植存储过程几乎是不可能的,不过存储过程的自我调用可以相对保持可移植
    • 编写存储过程比编写基本SQL语句复杂,需要更高的技能,更丰富的经验

      (5)存储过程的创建

    create procedure procedure_name(in arg1 type1, out argN typeN)
    begin
        ...
    end

      存储过程可以定义输入参数,用in关键字来表示;也可以定义输出参数,用out关键字来表示。存储过程的业务代码都放置在begin和end语句中,并且每天语句的结束法默认是分号;通过declare来声明变量,并且所有的变量声明都要放在代码块中的开头;通过set或者select...into来给变量赋值;此外,也可以通过select语句显示返回的值。整体来说,重要掌握了存储过程的相关语法(可以参考该链接),撰写业务逻辑代码还是不难的。

      (6)存储过程的执行

    -- MySQL 执行存储过程
    CALL procedure_name([param1 [, ...]])
    
    
    -- SQL Server 执行存储过程
    EXECUTE [ @RETURN STATUS =] procedure_name [[[@param1_name = ] VALUE | [@param2_name = ] @VARIABLE [ OUTPUT ]]
    [WITH RECOMPILE]
    
    
    -- Oracle 执行存储过程
    EXECUTE [ @RETURN STATUS =] procedure_name [[[@param1_name = ] VALUE | [@param2_name = ] @VARIABLE [ OUTPUT ]]
    [WITH RECOMPILE]

      在MySQL中执行存储过程的方式是:call database_name.procedure_name(arg1,...,argN),其实也可以不用数据库名,直接用存储过程名,即call procedure_name(arg1,...,argN)

      (7)代码注释

      在写存储过程中应该添加适当的注释,这样更容易地理解和更安全地修改代码;增加注释不影响性能,也不存在缺陷。对代码行进行注释的标准方式是在之前放置两个连字符(--),注意了连字符和注释内容之间至少要隔一个空格;所有的DBMS都支持--连字符进行注释。MySQL还支持井号(#)进行注释。

      (8)触发器

      触发器是特殊的存储过程,它在特定的数据库活动发生时自动执行。触发器可以与特定表上的insert、update和delete操作(或组合)相关联。在大多数情况下,触发器是很不错的函数,但是它会导致更多的I/O开销。

    • 存储过程只是简单的存储SQL语句,触发器与单个的表相关联。
    • 触发器的内容不能修改,只能替换或者重新创建它。
    • 不同的DBMS,它们的触发器操作时机可能不同,有的是在特定操作执行之前执行,有的是在特定操作执行之后执行。
    • 触发器内的代码数据访问权:
      • insert操作中的所有新数据
      • update操作中的所有新数据和旧数据
      • delete操作中删除的数据
    • 触发器的用途
      • 保证数据一致。
      • 基于某个表的变动在其他表上执行活动。
      • 进行额外的验证并根据需要回退数据。
      • 计算列的值或更新时间戳。
    • 一般来说,约束的处理比触发器快,应尽量使用约束。

      2、游标简介

      (1)游标:也称为光标,是一个存储在DBMS服务器上的数据库查询,它不是一条select语句,而是被该语句检索出来的结果集。

      (2)用途:对检索出来的数据进行行前进或者后退操作,主要用于交互式应用,如用户滚动屏幕上的数据

      (3)特性:

    • 能够标记游标为只读,使数据能读取,但不能更新和删除
    • 能控制可以执行的定向操作(向前、向后、第一、最后、绝对位置、相对位置等)
    • 能标记某些列为可编辑的,某些列为不可编辑的
    • 规定范围,使游标对创建它的特定请求(如存储过程)或对所有请求可访问
    • 只是DBMS对检索出的数据(而不是指出表中活动数据)进行复制,使数据在游标打开和访问期间不变化

      (4)支持游标的DBMS:DB2、MariaDB、MySQL 5、SQL Server、SQLite、Oracle和PostgreSQL,而Microsoft Access不支持

      (5)游标对基于Web的应用用处不大(ASP、ASP.NET、ColdFusion、PHP、Python、Ruby、JSP等),大多数Web应用开发人员不使用游标

      (6)使用:

    • 声明游标: DECLARE cursor_name CURSOR FOR SELECT * FROM table_name;  // 还没有检索数据
    -- MySQL游标的声明
    DECLARE cursor_name CURSOR FOR select_statement
    
    -- SQL Server游标的声明
    DECLARE cursor_name CURSOR FOR select_statement [FOR [READ ONLY | UPDATE {[co lumn_list]}]]
    
    -- Oracle游标的声明
    DECLARE CORSOR cursor_name IS {select_statement}
    • 打开游标:OPEN cursor_name; // 开始检索数据,即指定游标的SELECT语句被执行,并且查询的结果集被保存在内存里的特定区域。
    -- MySQL打开游标
    OPEN cursor_name
    
    
    -- SQL Server打开游标
    OPEN cursor_name
    
    
    -- Oracle打开游标
    OPEN cursor_name [param1 [, param2]]
    • 获取数据:FETCH cursor_name into var1,var2,...,varn; // 当游标cursor_name检索完数据后,只有等到下一次fetch时才会触发结束的标志
    -- MySQL游标获取数据
    FETCH cursor_name INTO var1_name [, var2_name] ...
    
    
    -- SQL Server游标获取数据
    FETCH NEXT FROM cursor_name [INTO fetch_list]
    
    
    -- Oracle游标获取数据
    FETCH cursor_name  {INTO : host_var1 [[INDICATOR] : indicator_var1] [, : host_var2 [[INDICATOR] : indicator_var2]] | USING DESCRIPTOR DESCRIPTOR}
    • 关闭游标:CLOSE cursor_name;
    -- MySQL关闭游标,会主动释放资源,所以不需要DEALLOCATE语句
    CLOSE cursor_name
    
    
    -- SQL Server关闭游标和释放资源
    CLOSE cursor_name
    DEALLOCATE cursor_name
    
    
    -- Oracle关闭游标,会主动释放资源,所以不需要DEALLOCATE语句
    CLOSE cursor_name

      3、存储过程代码示例

      (1)主存储过程:该存储过程的名称为main_procedure_name,并且在该存储过程中调用另外一个存储过程,其名称为child_procedure_name

    CREATE  PROCEDURE `database_name`.`main_procedure_name`()
    begin
        declare tmp_id varchar(15);
        declare done int default false;
        declare tmp_cursor cursor for
            select distinct pk_id
            from database_name.table_name
            where `some_field` = 'some_value'; // 根据实际情况添加限定条件
        declare continue handler for not found set done = true;
        open tmp_cursor;
            pk_id_loop:loop
                fetch tmp_cursor into tmp_id;
                if done then
                    leave pk_id_loop;
                end if;
                call database_name.child_procedure_name(tmp_id);
            end loop pk_id_loop;
        close account_cursor;   
    end

      (2)子存储过程:该存储过程的名称为child_procedure_name,其伪代码流程如下所示

    CREATE PROCEDURE `database_name`.`child_procedure_name`(in input_param varchar(15))
    begin
        declare counts int default 0;
        declare cmp_result int default 0;
        declare cur_id varchar(20);
        declare cur_value varchar(30);
        declare pre_id varchar(20);
        declare pre_value varchar(30);
        declare next_id varchar(20);
        declare next_value varchar(30);
        declare done int default false;
        declare cursor_name cursor for
            select id, value
            from database_name.table_name
            where some_field = input_param
            order by another_field desc;
        declare continue handler for not found set done = true;
        select count(*) into counts from database_name.table_name where some_field = input_param;
        if counts = 1 then
            open cursor_name;
                fetch cursor_name into cur_id, cur_value;
                ...
            close cursor_name;
        elseif credit_count = 2 then
            open cursor_name;
                fetch cursor_name into cur_id, cur_value;
                fetch cursor_name into pr_id, pre_value;
                ...
            close cursor_name;
        elseif credit_count > 2 then
            open cursor_name;
            info_loop:loop
                fetch cursor_name into cur_id, cur_value;
                fetch cursor_name into pre_id, pre_value;
                ...    
                set next_id = pre_id;
                set next_value = pre_value;
                leave info_loop;
            end loop info_loop;
            close cursor_name;;
        end if; 
    end

      子存储过程的改造,不用区分总记录数为1、2和更多条的情况,以下是改造后的伪代码流程:

    CREATE PROCEDURE `database_name`.`child_procedure_name`(in input_param varchar(15))
    begin
        declare counts int default 0;
        declare cmp_result int default 0;
        declare cur_id varchar(20);
        declare cur_value varchar(30);
        declare pre_id varchar(20);
        declare pre_value varchar(30);
        declare next_id varchar(20);
        declare next_value varchar(30);
        declare flag int default 0;
        declare done int default false;
        declare cursor_name cursor for
            select id, value
            from database_name.table_name
            where some_field = input_param
            order by another_field desc;
        declare continue handler for not found set done = true;
        select count(*) into counts from database_name.table_name where some_field = input_param;
        set flag = counts % 2;
        open cursor_name;
        info_loop:loop
                fetch cursor_name into cur_id, cur_value;
                fetch cursor_name into pre_id, pre_value;
                if done then
                    # 只有一条记录数据
                    # 记录数据为偶数条
                    # 记录数据为奇数条
                     leave info_loop;
                end if;
                ...    
                set next_id = pre_id;
                set next_value = pre_value;
                leave info_loop;
        end loop info_loop;
        close cursor_name;;
    end
    -- 注意了,在跳出循环的地方需要进行commit提交操作

      子存储过程再次改造,每次循环只获取一条数据,跟上一次保存的数据进行比较,伪代码如下所示:

    CREATE PROCEDURE `database_name`.`child_procedure_name`(in input_param varchar(15))
    begin
        declare cmp_result int default 0;
        declare cur_id varchar(20);
        declare cur_value varchar(30);
        declare pre_id varchar(20);
        declare pre_value varchar(30);
        declare next_id varchar(20);
        declare next_value varchar(30);
        declare done int default false;
        declare cursor_name cursor for
            select id, value
            from database_name.table_name
            where some_field = input_param
            order by another_field desc;
        declare continue handler for not found set done = true;
        open cursor_name;
        info_loop:loop
                fetch cursor_name into cur_id, cur_value;
                if done then
                     ...
                     leave info_loop;
                end if;
                ...    
                set next_id = pre_id;
                set next_value = pre_value;
                leave info_loop;
        end loop info_loop;
        close cursor_name;;
    end

      上述的存储过程代码只是一个业务逻辑思路过程,可供参考,比如可以用来循环处理或者比较相邻两条记录的数据。在业务处理模块中,每次循环取一条数据的逻辑代码要简单些,但是循环的次数较多,具体的循环次数为总记录数+1;而每次循环取两条数据的逻辑代码要复杂些,不过循环的次数是单条数据循环次数的一般,具体的循环次数为总记录数 / 2 + 1,时间效率更高些。

     

  • 相关阅读:
    error: device not found
    xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Deve
    联想X系列服务器
    华为服务器
    linux db2升级
    aix6.1升级openssh&&openssl
    upgrading mysql: error: 1102: Incorrect database name
    linux7配置yum网络源
    How to install fixpack on DB2
    mysql 表空间管理
  • 原文地址:https://www.cnblogs.com/bien94/p/12811811.html
Copyright © 2020-2023  润新知