zoukankan      html  css  js  c++  java
  • mysql全家桶(四)存储过程

    一、存储过程
    1、介绍
    简单的说,就是一组SQL语句集,功能强大,可以实现一些比较复杂的逻辑功能,类似于JAVA语言中的方法;

    存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。

    存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。

    存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

    2、优缺点
    优点:
    存储过程可封装,并隐藏复杂的商业逻辑。
    存储过程可以回传值,并可以接受参数。
    存储过程无法使用 SELECT 指令来运行,因为它是子程序,与查看表,数据表或用户定义函数不同。
    存储过程可以用在数据检验,强制实行商业逻辑等。
    在生产环境下,可以通过直接修改存储过程的方式修改业务逻辑(或bug),而不用重启服务器(人直接就在正式服务器上修改存储过程,而没有经过完整的测试,后果非常严重。)

    执行速度快。存储过程经过编译之后会比单独一条一条执行要快。但这个效率真是没太大影响。如果是要做大数据量的导入、同步,我们可以用其它手段。

    减少网络传输。存储过程直接就在数据库服务器上跑,所有的数据访问都在服务器内部进行,不需要传输数据到其它终端。但我们的应付服务器通常与数据库是在同一内网,大数据的访问的瓶颈会是硬盘的速度,而不是网速。

    能够解决presentation(把属性映射到字段)与数据之间的差异(解决OO模型与二维数据持久化之间的阻抗)

    缺点:
    存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。
    存储过程的性能调校与撰写,受限于各种数据库系统。
    不便于调试。基本上没有较好的调试器

    没办法应用缓存。虽然有全局临时表之类的方法可以做缓存

    无法适应数据库的切割(水平或垂直切割)。数据库切割之后,存储过程并不清楚数据存储在哪个数据库中。

    3、创建
    *创建
    #创建
    CREATE PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_body

    CREATE PROCEDURE 过程名([[IN|OUT|INOUT] 参数名 数据类型[,[IN|OUT|INOUT] 参数名 数据类型…]]) [特性 ...] 过程体
    其中,sp_name参数是存储过程的名称;proc_parameter表示存储过程的参数列表; characteristic参数指定存储过程的特性;routine_body参数是SQL代码的内容,可以用BEGIN…END来标志SQL代码的开始和结束。

    *分隔符
    MySQL默认以";"为分隔符,如果没有声明分割符,则编译器会把存储过程当成SQL语句进行处理,因此编译过程会报错,所以要事先用“DELIMITER //”声明当前段分隔符,让编译器把两个"//"之间的内容当做存储过程的代码,不会执行这些代码;“DELIMITER ;”的意为把分隔符还原。

    DELIMITER $$ #将语句的结束符号从分号;临时改为两个$$(可以是自定义)
    存储过程语句
    $$
    DELIMITER;



    DELIMITER //
    存储过程语句
    //
    DELIMITER ;
    *参数(参数类型、参数名、数据类型)
    存储过程根据需要可能会有输入、输出、输入输出参数,如果有多个参数用","分割开。

    a.参数类型

    MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT:

    IN输入参数,不被输出
    OUT:不用输入,直接输出
    INOUT:输入,执行后输出
    b.参数名

    可以自定义

    c.数据类型(见mysql全家桶(一)数据库以及表操作的字段类型)

    d.示例

    #示例

    DELIMITER //
    CREATE PROCEDURE num_from_employee (IN emp_id INT, OUT count_num INT )
    READS SQL DATA
    BEGIN
    SELECT COUNT(*) INTO count_num
    FROM employee
    WHERE d_id=emp_id ;
    END
    //
    DELIMITER;
    创建 num_from_employee存储过程,传入整型emp_id,返回整型count_num

    *characteristict存储过程的特性
    参数有多个取值。其取值说明如下:

    LANGUAGE SQL:说明routine_body部分是由SQL语言的语句组成,这也是数据库系统默认的语言。
    [NOT] DETERMINISTIC:指明存储过程的执行结果是否是确定的。DETERMINISTIC表示结果是确定的。每次执行存储过程时,相同的输入会得到相同的输出。NOT DETERMINISTIC表示结果是非确定的,相同的输入可能得到不同的输出。默认情况下,结果是非确定的。
    { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }:指明子程序使用SQL语句的限制。CONTAINS SQL表示子程序包含SQL语句,但不包含读或写数据的语句;NO SQL表示子程序中不包含SQL语句;READS SQL DATA表示子程序中包含读数据的语句;MODIFIES SQL DATA表示子程序中包含写数据的语句。默认情况下,系统会指定为CONTAINS SQL。
    SQL SECURITY { DEFINER | INVOKER }:指明谁有权限来执行。DEFINER表示只有定义者自己才能够执行;INVOKER表示调用者可以执行。默认情况下,系统指定的权限是DEFINER。
    COMMENT 'string':注释信息。
    *routine_body过程体
    routine_body参数是SQL代码的内容,可以用BEGIN…END来标志SQL代码的开始和结束。

    存储过程体包含了在过程调用时必须执行的语句,例如:dml(数据操纵语言、INSERT、UPDATE、DELETE)、ddl语句(数据库模式定义语言create、alter),if-then-else和while-do语句、声明变量的declare语句等
    过程体格式:以begin开始,以end结束(可嵌套)
    BEGIN
      BEGIN
        BEGIN
          statements;
        END
      END
    END
    注意:每个嵌套块及其中的每条语句,必须以分号结束,表示过程体结束的begin-end块(又叫做复合语句compound statement),则不需要分号。

    为语句块贴标签:

    [begin_label:] BEGIN
      [statement_list]
    END [end_label]
    #例如
    label1: BEGIN
      label2: BEGIN
        label3: BEGIN
          statements;
        END label3 ;
      END label2;
    END label1
    标签有两个作用:

    1、增强代码的可读性
    2、在某些语句(例如:leave和iterate语句),需要用到标签
    变量

    局部变量声明一定要放在存储过程体的开始:

    #定义
    DECLARE variable_name [,variable_name...] datatype [DEFAULT value];

    #例如(variable_name变量名、datatype 为 MySQL 的数据类型,如: int, float, date,varchar(length))

    DECLARE l_int int unsigned default 4000000;
    DECLARE l_numeric number(8,2) DEFAULT 9.95;
    DECLARE l_date date DEFAULT '1999-12-31';
    DECLARE l_datetime datetime DEFAULT '1999-12-31 23:59:59';
    DECLARE l_varchar varchar(255) DEFAULT 'This will not be padded';
    #变量赋值
    SET 变量名 = 表达式值 [,variable_name = expression ...]

    #例如
    SET var_name = expr [, var_name = expr] ...

    #MySQL中还可以使用SELECT…INTO语句为变量赋值。其基本语法如下:
    SELECT col_name[,…] INTO var_name[,…] FROM table_name WEHRE condition

    #其中,col_name参数表示查询的字段名称;var_name参数是变量的名称;table_name参数指表的名称;condition参数指查询条件。
     用户变量

    基于会话变量实现的,可以暂存值,并传递给同一连接里的下一条sql使用的变量,当客户端连接退出时,变量会被释放。

    #在MySQL客户端使用用户变量
    SELECT 'Hello World' into @x;
    SELECT @x;

    #输出
    +-------------+
    | @x |
    +-------------+
    | Hello World |
    +-------------+

    #在存储过程中使用用户变量
    CREATE PROCEDURE GreetWorld( ) SELECT CONCAT(@greeting,' World');
    SET @greeting='Hello';
    CALL GreetWorld( );

    #输出
    +----------------------------+
    | CONCAT(@greeting,' World') |
    +----------------------------+
    | Hello World |
    +----------------------------+

    #在存储过程间传递全局范围的用户变量
    CREATE PROCEDURE p1() SET @last_procedure='p1';
    CREATE PROCEDURE p2() SELECT CONCAT('Last procedure was ',@last_procedure);
    CALL p1( );
    CALL p2( );

    #输出
    +-----------------------------------------------+
    | CONCAT('Last procedure was ',@last_proc |
    +-----------------------------------------------+
    | Last procedure was p1 |
    +-----------------------------------------------+
    定义条件

    MySQL中可以使用DECLARE关键字来定义条件。其基本语法如下:

    DECLARE condition_name CONDITION FOR condition_value
    condition_value:
    SQLSTATE [VALUE] sqlstate_value | mysql_error_code
    其中,condition_name参数表示条件的名称;condition_value参数表示条件的类型;sqlstate_value参数和mysql_error_code参数都可以表示MySQL的错误。例如ERROR 1146 (42S02)中,sqlstate_value值是42S02,mysql_error_code值是1146。

    #例如

    //方法一:使用sqlstate_value
    DECLARE can_not_find CONDITION FOR SQLSTATE '42S02' ;
    //方法二:使用mysql_error_code
    DECLARE can_not_find CONDITION FOR 1146 ;
    定义处理程序

    MySQL中可以使用DECLARE关键字来定义处理程序。其基本语法如下:

    DECLARE handler_type HANDLER FOR
    condition_value[,...] sp_statement
    handler_type:
    CONTINUE | EXIT | UNDO
    condition_value:
    SQLSTATE [VALUE] sqlstate_value |
    condition_name | SQLWARNING
    | NOT FOUND | SQLEXCEPTION | mysql_error_code
    其中,handler_type参数指明错误的处理方式,该参数有3个取值。这3个取值分别是CONTINUE、EXIT和UNDO。CONTINUE表示遇到错误不进行处理,继续向下执行;EXIT表示遇到错误后马上退出;UNDO表示遇到错误后撤回之前的操作,MySQL中暂时还不支持这种处理方式。

      注意:通常情况下,执行过程中遇到错误应该立刻停止执行下面的语句,并且撤回前面的操作。但是,MySQL中现在还不能支持UNDO操作。因此,遇到错误时最好执行EXIT操作。如果事先能够预测错误类型,并且进行相应的处理,那么可以执行CONTINUE操作。

      condition_value参数指明错误类型,该参数有6个取值。sqlstate_value和mysql_error_code与条件定义中的是同一个意思。condition_name是DECLARE定义的条件名称。SQLWARNING表示所有以01开头的sqlstate_value值。NOT FOUND表示所有以02开头的sqlstate_value值。SQLEXCEPTION表示所有没有被SQLWARNING或NOT FOUND捕获的sqlstate_value值。sp_statement表示一些存储过程或函数的执行语句。

    #示例
    //方法一:捕获sqlstate_value
    DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
    SET @info='CAN NOT FIND';
    //方法二:捕获mysql_error_code
    DECLARE CONTINUE HANDLER FOR 1146 SET @info='CAN NOT FIND';
    //方法三:先定义条件,然后调用
    DECLARE can_not_find CONDITION FOR 1146 ;
    DECLARE CONTINUE HANDLER FOR can_not_find SET
    @info='CAN NOT FIND';
    //方法四:使用SQLWARNING
    DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';
    //方法五:使用NOT FOUND
    DECLARE EXIT HANDLER FOR NOT FOUND SET @info='CAN NOT FIND';
    //方法六:使用SQLEXCEPTION
    DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';
     第一种方法是捕获sqlstate_value值。如果遇到sqlstate_value值为42S02,执行CONTINUE操作,并且输出"CAN NOT FIND"信息。

      第二种方法是捕获mysql_error_code值。如果遇到mysql_error_code值为1146,执行CONTINUE操作,并且输出"CAN NOT FIND"信息。

      第三种方法是先定义条件,然后再调用条件。这里先定义can_not_find条件,遇到1146错误就执行CONTINUE操作。

      第四种方法是使用SQLWARNING。SQLWARNING捕获所有以01开头的sqlstate_value值,然后执行EXIT操作,并且输出"ERROR"信息。

      第五种方法是使用NOT FOUND。NOT FOUND捕获所有以02开头的sqlstate_value值,然后执行EXIT操作,并且输出"CAN NOT FIND"信息。

      第六种方法是使用SQLEXCEPTION。SQLEXCEPTION捕获所有没有被SQLWARNING或NOT FOUND捕获的sqlstate_value值,然后执行EXIT操作,并且输出"ERROR"信息。

    注释

    MySQL 存储过程可使用两种风格的注释

    两个横杆--:该风格一般用于单行注释。

    c 风格: 一般用于多行注释。/*注释*/

    #例如
    DELIMITER //
    CREATE PROCEDURE proc1 --name存储过程名
    -> (IN parameter1 INTEGER)
    -> BEGIN
    -> DECLARE variable1 CHAR(10);
    -> IF parameter1 = 17 THEN
    -> SET variable1 = 'birds';
    -> ELSE
    -> SET variable1 = 'beasts';
    -> END IF;
    -> INSERT INTO table1 VALUES (variable1);
    -> END
    -> //
    DELIMITER ;
    4、调用
    用call和你过程名以及一个括号,括号里面根据需要,加入参数,参数包括输入参数、输出参数、输入输出参数。具体的调用方法可以参看上面的例子。

    call product(); //无参
    call procedure2(@userSum,@userAvg,201708); //有参
    5、查询
    查看某个数据库下面的存储过程

    showtables;

    或者

    selectname from mysql.proc where db='数据库名';

    或者

    selectroutine_name from information_schema.routines where routine_schema='数据库名';

    或者

    showprocedure status where db='数据库名';
    查看存储过程的详细

    SHOWCREATE PROCEDURE 数据库.存储过程名;
    6、修改
    ALTER PROCEDURE
    7、删除
    DROPPROCEDURE
    8、MySQL存储过程的控制语句
    (1). 变量作用域

    内部的变量在其作用域范围内享有更高的优先权,当执行到 end。变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储过程外再也不能找到这个申明的变量,但是你可以通过 out 参数或者将其值指派给会话变量来保存其值。

    DELIMITER //
    CREATE PROCEDURE proc3()
    begin
    declare x1 varchar(5) default 'outer';
    begin
    declare x1 varchar(5) default 'inner';
    select x1;
    end;
    elect x1;
    end;
    //
    DELIMITER ;
    (2). 条件语句

    1. if-then-else 语句

    DELIMITER //
    CREATE PROCEDURE proc2(IN parameter int)
    begin
    declare var int;
    set var=parameter+1;
    if var=0 then
    insert into t values(17);
    end if;
    if parameter=0 then
    update t set s1=s1+1;
    else
    update t set s1=s1+2;
    end if;
    end;
    //
    DELIMITER ;
    2. case语句:

    DELIMITER //
    CREATE PROCEDURE proc3 (in parameter int)
    begin
    declare var int;
    set var=parameter+1;
    case var
    when 0 then
    insert into t values(17);
    when 1 then
    insert into t values(18);
    else
    insert into t values(19);
    end case;
    end;
    //
    DELIMITER ;

    #结构
    case
    when var=0 then
    insert into t values(30);
    when var>0 then
    when var<0 then
    else
    end case
    (3). 循环语句

    1. while ···· end while

    DELIMITER //
    CREATE PROCEDURE proc4()
    begin
    declare var int;
    set var=0;
    while var<6 do
    insert into t values(var);
    set var=var+1;
    end while;
    end;
    //
    DELIMITER ;
    while 条件 do
    --循环体
    endwhile
    2. repeat···· end repea

    它在执行操作后检查结果,而 while 则是执行前进行检查。

    DELIMITER //
    CREATE PROCEDURE proc5 ()
    begin
    declare v int;
    set v=0;
    repeat
    insert into t values(v);
    set v=v+1;
    until v>=5
    end repeat;
    end;
    //
    DELIMITER ;
    repeat
    --循环体
    until 循环条件
    end repeat;
    3. loop ·····endloop

    loop 循环不需要初始条件,这点和 while 循环相似,同时和 repeat 循环一样不需要结束条件, leave 语句的意义是离开循环。

    DELIMITER //
    CREATE PROCEDURE proc6 ()
    begin
    declare v int;
    set v=0;
    LOOP_LABLE:loop
    insert into t values(v);
    set v=v+1;
    if v >=5 then
    leave LOOP_LABLE;
    end if;
    end loop;
    end;
    //
    DELIMITER ;
    4. LABLES 标号:

    标号可以用在 begin repeat while 或者 loop 语句前,语句标号只能在合法的语句前面使用。可以跳出循环,使运行指令达到复合语句的最后一步。

    (4). ITERATE迭代

    ITERATE 通过引用复合语句的标号,来从新开始复合语句:

    DELIMITER //
    CREATE PROCEDURE proc10 (http://www.my516.com)
    begin
    declare v int;
    set v=0;
    LOOP_LABLE:loop
    if v=3 then
    set v=v+1;
    ITERATE LOOP_LABLE;
    end if;
    insert into t values(v);
    set v=v+1;
    if v>=5 then
    leave LOOP_LABLE;
    end if;
    end loop;
    end;
    //
    DELIMITER ;
     

     
    ---------------------

  • 相关阅读:
    关于浏览器的内核 版本 发展 详解
    js判断浏览器内核 及ie的版本问题
    插入flash代码
    大前端学习笔记【七】关于CSS再次整理
    其实,程序员没有我想象得那么简单(算是2016的简单总结吧...)
    大前端学习笔记整理【七】HTTP协议以及http与https的区别
    大前端学习笔记整理【六】this关键字详解
    大前端学习笔记整理【五】关于JavaScript中的关键字——this
    大前端学习笔记整理【五】rem与px换算的计算方式
    大前端学习笔记整理【四】LESS基础
  • 原文地址:https://www.cnblogs.com/ly570/p/11155035.html
Copyright © 2011-2022 走看看