• MySql存储过程的作用及语法


    MySQL使用存储过程的作用

    1、使用了存过程,很多相似性的删除,更新,新增等操作就变得轻松了,并且以后也便于管理!

    2、存储过程因为SQL语句已经预编绎过了,因此运行的速度比较快。   

    3、存储过程可以接受参数、输出参数、返回单个或多个结果集以及返回值。可以向程序返回错误原因。     

    4、存储过程运行比较稳定,不会有太多的错误。只要一次成功,以后都会按这个程序运行。     

    5、存储过程主要是在服务器上运行,减少对客户机的压力。   

    6、存储过程可以包含程序流、逻辑以及对数据库的查询。同时可以实体封装和隐藏了数据逻辑。   

    7、存储过程可以在单个存储过程中执行一系列SQL语句。   

    8、存储过程可以从自己的存储过程内引用其它存储过程,这可以简化一系列复杂语句。

    MySQL使用存储过程的争议

    尽管存储过程有诸多优点,但是对于存储过程的使用,一直都存在着很多争议,比如有些公司对于大型项目要求使用存储过程,而有些公司在手册中明确禁止使用存储过程,为什么这些公司对存储过程的使用需求差别这么大呢?

    我们得从存储过程的特点来找答案。

    你能看到存储过程有很多好处。

    首先存储过程可以一次编译多次使用。存储过程只在创造时进行编译,之后的使用都不需要重新编译,这就提升了 SQL 的执行效率。其次它可以减少开发工作量。将代码封装成模块,实际上是编程的核心思想之一,这样可以把复杂的问题拆解成不同的模块,然后模块之间可以重复使用,在减少开发工作量的同时,还能保证代码的结构清晰。还有一点,存储过程的安全性强,我们在设定存储过程的时候可以设置对用户的使用权限,这样就和视图一样具有较强的安全性。最后它可以减少网络传输量,因为代码封装到存储过程中,每次使用只需要调用存储过程即可,这样就减少了网络传输量。同时在进行相对复杂的数据库操作时,原本需要使用一条一条的 SQL 语句,可能要连接多次数据库才能完成的操作,现在变成了一次存储过程,只需要连接一次即可。

    基于上面这些优点,不少大公司都要求大型项目使用存储过程,比如微软、IBM 等公司。但是国内的阿里并不推荐开发人员使用存储过程,这是为什么呢?

    存储过程虽然有诸如上面的好处,但缺点也是很明显的。

    它的可移植性差,存储过程不能跨数据库移植,比如在 MySQL、Oracle 和 SQL Server 里编写的存储过程,在换成其他数据库时都需要重新编写。

    其次调试困难,只有少数 DBMS 支持存储过程的调试。对于复杂的存储过程来说,开发和维护都不容易。

    此外,存储过程的版本管理也很困难,比如数据表索引发生变化了,可能会导致存储过程失效。我们在开发软件的时候往往需要进行版本管理,但是存储过程本身没有版本控制,版本迭代更新的时候很麻烦。

    最后它不适合高并发的场景,高并发的场景需要减少数据库的压力,有时数据库会采用分库分表的方式,而且对可扩展性要求很高,在这种情况下,存储过程会变得难以维护,增加数据库的压力,显然就不适用了。

    了解了存储过程的优缺点之后,我想说的是,存储过程既方便,又有局限性。尽管不同的公司对存储过程的态度不一,但是对于我们开发人员来说,不论怎样,掌握存储过程都是必备的技能之一。

    MySQL存储过程的创建 

    先定义一个结束分隔符,控制sql语句的执行

    delimiter $      //意思是用$作为sql语句的结束符

    参数 

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

    CREATE PROCEDURE([[IN |OUT |INOUT ] 参数名 数据类形...]) 

    IN 输入参数:表示该参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值 

    OUT 输出参数:该值可在存储过程内部被改变,并可返回 

    INOUT 输入输出参数:调用时指定,并且可被改变和返回 

    ①IN参数例子 

    创建: 

    DELIMITER $ 
    CREATE PROCEDURE demo(IN p_in int) 
     BEGIN 
     SELECT p_in; 
     SET p_in=2; 
     SELECT p_in; 
    END$

    执行结果: 

    SET @p_in=1;
    CALL demo(@p_in);
    +------+ 
    | p_in | 
    +------+ 
    | 1    | 
    +------+ 
    +------+ 
    | p_in | 
    +------+ 
    | 2    | 
    +------+
    SELECT @p_in;
    +-------+ 
    | @p_in | 
    +-------+ 
    |  1    | 
    +-------+

    以上可以看出,p_in虽然在存储过程中被修改,但并不影响@p_id的值 

    ②OUT参数例子 

    创建: 

    DELIMITE $
    CREATE PROCEDURE demo(OUT p_out int) 
     BEGIN 
     SELECT p_out; 
     SET p_out=2; 
     SELECT p_out; 
     END$

    执行结果: 

    SET @p_out=1; 
    CALL demo(@p_out);
    +-------+ 
    | p_out | 
    +-------+ 
    | NULL  | 
    +-------+ 
    +-------+ 
    | p_out | 
    +-------+ 
    | 2     | 
    +-------+
    SELECT @p_out; 
    +-------+ 
    | p_out | 
    +-------+ 
    | 2     | 
    +-------+

    ③INOUT参数例子 

    创建: 

    DELIMITER $
    CREATE PROCEDURE demo_inout_parameter(INOUT p_inout int) 
     BEGIN 
     SELECT p_inout; 
     SET p_inout=2; 
     SELECT p_inout; 
     END$

    执行结果: 

    SET @p_inout=1; 
    CALL demo_inout_parameter(@p_inout) ;
    +---------+ 
    | p_inout | 
    +---------+ 
    | 1       | 
    +---------+ 
    +---------+ 
    | p_inout | 
    +---------+ 
    | 2       | 
    +---------+
    SELECT @p_inout;
    +----------+ 
    | @p_inout | 
    +----------+ 
    | 2        | 
    +----------+

    变量

    ①定义变量

    例如: 

    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 ...] 

    ③用户变量 

    ⅰ. 在MySQL客户端使用用户变量 

    SELECT 'Hello World' into @x; 
    SELECT @x; 
    +-------------+ 
    | @x          | 
    +-------------+ 
    | Hello World | 
    +-------------+ 
    SET @y='Goodbye Cruel World'; 
    SELECT @y; 
    +---------------------+ 
    | @y               | 
    +---------------------+ 
    | Goodbye Cruel World | 
    +---------------------+ 
    SET @z=1+2+3; 
    SELECT @z; 
    +------+ 
    | @z   | 
    +------+ 
    | 6    | 
    +------+

    ⅱ. 在存储过程中使用用户变量 

    CREATE PROCEDURE GreetWorld( ) 
     BEGIN
     SELECT CONCAT(@greeting,' World'); 
     SET @greeting='Hello'; 
     END$
     CALL GreetWorld( ); 
    +----------------------------+ 
    | CONCAT(@greeting,' World') | 
    +----------------------------+ 
    | Hello World              | 
    +----------------------------+

    ④MySQL存储过程的修改 

    ALTER PROCEDURE

    更改用CREATE PROCEDURE 建立的预先指定的存储过程,其不会影响相关存储过程或存储功能。 

    ⑤MySQL存储过程的删除 

    删除一个存储过程比较简单,和删除表一样: 

    DROP PROCEDURE

    从MySQL的表格中删除一个或多个存储过程。

    ⑥MySQL存储过程的控制语句 

    (1). 变量作用域 

    内部的变量在其作用域范围内享有更高的优先权,当执行到end。变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储 

    过程外再也不能找到这个申明的变量,但是你可以通过out参数或者将其值指派 

    给会话变量来保存其值。 

    CREATE PROCEDURE proc3() 
     begin 
     declare x1 varchar(5) default 'outer'; 
     begin 
     declare x1 varchar(5) default 'inner'; 
     select x1; 
     end; 
     select x1; 
     end$

    (2). 条件语句 

    A:if-then -else语句 

    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$

    B:case语句: 

    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$

    C:loop ·····end loop: 

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

    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$

    D:while语句    while ···· end while:

    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$

    E:repeat 语句       repeat···· end repeat:

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

    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$

    ⑦ITERATE迭代 

     ITERATE: 

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

    CREATE PROCEDURE proc10 () 
     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$

    MySQL存储过程的基本函数

    (1).字符串类 

    CHARSET(str)                     //返回字串字符集 
    CONCAT (string2 [,... ])         //连接字串 
    INSTR (string ,substring )       //返回substring首次在string中出现的位置,不存在返回0 
    LCASE (string2 )                 //转换成小写 
    LEFT (string2 ,length )          //从string2中的左边起取length个字符 
    LENGTH (string )                 //string长度 
    LOAD_FILE (file_name )           //从文件读取内容 
    LOCATE (substring , string [,start_position ] ) 同INSTR,但可指定开始位置 
    LPAD (string2 ,length ,pad )     //重复用pad加在string开头,直到字串长度为length 
    LTRIM (string2 )                 //去除前端空格 
    REPEAT (string2 ,count )         //重复count次 
    REPLACE (str ,search_str ,replace_str ) //在str中用replace_str替换search_str 
    RPAD (string2 ,length ,pad)       //在str后用pad补充,直到长度为length 
    RTRIM (string2 )                  //去除后端空格 
    STRCMP (string1 ,string2 )         //逐字符比较两字串大小, 
    SUBSTRING (str , position [,length ]) //从str的position开始,取length个字符

    注:mysql中处理字符串时,默认第一个字符下标为1,即参数position必须大于等于1 

    select substring('abcd',0,2); 
    +-----------------------+ 
    | substring('abcd',0,2) | 
    +-----------------------+ 
    +-----------------------+
    select substring('abcd',1,2); 
    +-----------------------+ 
    | substring('abcd',1,2) | 
    +-----------------------+ 
    | ab                | 
    +-----------------------+
    TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符 
    UCASE (string2 ) //转换成大写 
    RIGHT(string2,length) //取string2最后length个字符 
    SPACE(count) //生成count个空格

    (2).数学类 

    ABS (number2 ) //绝对值 
    BIN (decimal_number ) //十进制转二进制 
    CEILING (number2 ) //向上取整 
    CONV(number2,from_base,to_base) //进制转换 
    FLOOR (number2 ) //向下取整 
    FORMAT (number,decimal_places ) //保留小数位数 
    HEX (DecimalNumber ) //转十六进制

    注:HEX()中可传入字符串,则返回其ASC-11码,如HEX('DEF')返回4142143 

    也可以传入十进制整数,返回其十六进制编码,如HEX(25)返回19 

    LEAST (number , number2 [,..]) //求最小值 
    MOD (numerator ,denominator ) //求余 
    POWER (number ,power ) //求指数 
    RAND([seed]) //随机数 
    ROUND (number [,decimals ]) //四舍五入,decimals为小数位数]

    注:返回类型并非均为整数,如: 

    (1)默认变为整形值 

    select round(1.23); 
    +-------------+ 
    | round(1.23) | 
    +-------------+ 
    | 1           | 
    +-------------+
    select round(1.56); 
    +-------------+ 
    | round(1.56) | 
    +-------------+ 
    | 2           | 
    +-------------+

    (2)可以设定小数位数,返回浮点型数据 

    select round(1.567,2); 
    +----------------+ 
    | round(1.567,2) | 
    +----------------+ 
    | 1.57           | 
    +----------------+

    (3).日期时间类 

    ADDTIME (date2 ,time_interval ) //将time_interval加到date2 
    CONVERT_TZ (datetime2 ,fromTZ ,toTZ ) //转换时区 
    CURRENT_DATE ( ) //当前日期 
    CURRENT_TIME ( ) //当前时间 
    CURRENT_TIMESTAMP ( ) //当前时间戳 
    DATE (datetime ) //返回datetime的日期部分 
    DATE_ADD (date2 , INTERVAL d_value d_type ) //在date2中加上日期或时间 
    DATE_FORMAT (datetime ,FormatCodes ) //使用formatcodes格式显示datetime 
    DATE_SUB (date2 , INTERVAL d_value d_type ) //在date2上减去一个时间 
    DATEDIFF (date1 ,date2 ) //两个日期差 
    DAY (date ) //返回日期的天 
    DAYNAME (date ) //英文星期 
    DAYOFWEEK (date ) //星期(1-7) ,1为星期天 
    DAYOFYEAR (date ) //一年中的第几天 
    EXTRACT (interval_name FROM date ) //从date中提取日期的指定部分 
    MAKEDATE (year ,day ) //给出年及年中的第几天,生成日期串 
    MAKETIME (hour ,minute ,second ) //生成时间串 
    MONTHNAME (date ) //英文月份名 
    NOW ( ) //当前时间 
    SEC_TO_TIME (seconds ) //秒数转成时间 
    STR_TO_DATE (string ,format ) //字串转成时间,以format格式显示 
    TIMEDIFF (datetime1 ,datetime2 ) //两个时间差 
    TIME_TO_SEC (time ) //时间转秒数] 
    WEEK (date_time [,start_of_week ]) //第几周 
    YEAR (datetime ) //年份 
    DAYOFMONTH(datetime) //月的第几天 
    HOUR(datetime) //小时

    视频学习中的例子

    1

    create procedure p1()
    begin
        select 'hello' from dual;
    end$
    call p1()$

    create procedure p2()
    begin
    declare age int default 18;
    declare height int default 180;
    select concat('年龄是',age,'身高是',height);
    end$

    3

    create procedure p3()
    begin
    declare age int default 18;
    set age:= age+20;
    select concat('20年后年龄是',age);
    end$

    4

    create procedure p4()
    begin
    declare age int default 18;
    if age >=18 then
    select  '成年';
    else
    select '未成年';
    end if;
    end$

    5

    create procedure p5(width int ,height int)
    begin
    select concat('你的面积是', width * height ) as area;
    if width >height then
    select '你挺胖';;
    elseif width < height then
    select ‘你挺瘦’;
    else
    select '你挺方';
    endif;
    end$

    6

    create procedure p6()
    begin
    declare total int default 0;
    declare num int default 0;
    while num <100 do
    set num:=num+1;
    set total:= total +num;
    end while;
    select total;
    end$

    7

    create procedure p7(in n int)
    begin
    declare total int default 0;
    declare num int default 0;
    while num <n do
    set num:=num+1;
    set total:= total +num;
    end while;
    select total;
    end$

    8

    create procedure p8(in n int ,out total int)
    begin
    declare num int default 0;
    set total :=0; #null  碰到任何操作都会变成null
    while num<n do
    set num:=num+1;
    set total :=total+num;
    end while;
    end$
    call p8(100,@summary)$
    select @summary

    9

    create procedure p9(inout age int)
    begin
    set age:=age +20;
    end$
    set @currage =18$
    call p9(@currage)$
    select @currage$

    10

    create procedure p10()
    begin
    declare pos int default 0;
    set pos := floor(5*rand());
    case pos
    when 1 then select 'still flying';
    when 2 then select 'fall in sea';
    when 3 then select 'in the island';
    else select 'I don't know';
    end case;
    end$

    11

    create procedure p11()
    begin
    declare total int default 0;
    declare i int default 0;
    repeat 
    set i:=i+1;
    set total :=total +i;
    until i>=100 end repeat;
    select total;
    end$

    12  游标 

    create procedure p12()
    begin
    declare row_gid int;
    declare row_num int;
    declare row_name varchar(20);
    declare getgoods cursor for select gid,num,name from goods;
    open getgoods;
    fetch getgoods into row_gid,row_num,row_name;
    select row_num,row_name;
    close getgoods;
    end$

    13

    create procedure p13()
    begin
    declare row_gid int;
    declare row_num int;
    declare row_name varchar(20);
    declare getgoods cursor for select gid, num , name from goods;
    open getgoods;
    fetch getgoods into row_gid,row_num,row_name; #cat
    select row_num,row_name;
    fetch getgoods into row_gid,row_num,row_name;#dog
    select row_num,row_name;
    fetch getgoods into row_gid,row_num,row_name;#pig
    select row_num,row_name;
    fetch getgoods into row_gid,row_num,row_name;#error
    select row_num,row_name;
    close getgoods;
    end$

    #逻辑处理游标越界

    14

    create procedure p14()
    begin
    declare row_gid int;
    declare row_num int;
    declare row_name varchar(20);
    declare cnt int default 0;
    declare i int default 0;
    declare getgoods cursor for select gid, num , name from goods;
    select count(*) into cnt from goods;
    open getgoods;
    repeat set 
    fetch getgoods into row_gid,row_num,row_name; #cat
    select row_num,row_name;
    until i >= cnt  end repeat;
    close getgoods;
    end$

    15

    游标越界的时候,在mysql  cursor中有一个标示,可以declare continue  handler 来操作一个越界标示

    declare continue handler for NOT FOUND statement;
    create procedure p15()
    begin
    declare row_gid int;
    declare row_num int;
    declare row_name varchar(20);
    declare you int default 1;
    declare getgoods cursor for select gid, num , name from goods;
    select count(*) into cnt from goods;
    declare continue handler for NOT FOUND set you :=0;  //如果发生not found事件,把you  改为 0
    open getgoods;
    repeat set 
    fetch getgoods into row_gid,row_num,row_name;
    select row_num,row_name;
    until you=0 end repeat;
    close getgoods;
    end$
    call p15()$
    show warnings$

    16:上条BUG修复,continue和 exit的区别(exit触发后后面的代码不在执行,而continue继续执行)

    create procedure p16()
    begin
    declare row_gid int;
    declare row_num int;
    declare row_name varchar(20);
    declare you int default 1;
    declare getgoods cursor for select gid, num , name from goods;
    select count(*) into cnt from goods;
    declare exit handler for NOT FOUND set you :=0;#这里的handler类型改成了exit
    open getgoods;
    repeat set 
    fetch getgoods into row_gid,row_num,row_name; 
    select row_num,row_name;
    until you=0 end repeat;
    close getgoods;
    end$

    17:正确严谨的逻辑

    #接下来的存储过程才应该是游标的正确使用方式:

    #一定要用continue handler  ,通过逻辑来控制。

    create procedure p17()
    begin
    declare row_gid int;
    declare row_num int;
    declare row_name varchar(20);
    declare you int default 1;
    declare getgoods cursor for select gid, num , name from goods;
    select count(*) into cnt from goods;
    declare continue handler for NOT FOUND set you :=0;
    open getgoods;
    fetch getgoods into row_gid,row_num,row_name; 
    repeat set 
    select row_num,row_name;
    fetch getgoods into row_gid,row_num,row_name; 
    until you=0 end repeat;
    close getgoods;
    end$

    #换成while 循环

    18

    create procedure p18()
    begin
    declare row_gid int;
    declare row_num int;
    declare row_name varchar(20);
    declare you int default 1;
    declare getgoods cursor for select gid, num , name from goods;
    select count(*) into cnt from goods;
    declare continue handler for NOT FOUND set you :=0;
    open getgoods;
    fetch getgoods into row_gid,row_num,row_name; 
    while you =1 do
    select row_num,row_name;
    fetch getgoods into row_gid,row_num,row_name; 
    end while;
    close getgoods;
    end$
  • 相关阅读:
    创建或者连接管道+++检查管道空间是否够写入本消息++++删除管道
    从instr中截取第一个delimiter之前的内容放到outstr中,返回第一个delimiter之后的位置
    把数字按网络顺序或主机顺序存放到字符串中++++把字符串按网络顺序转换成数字++++把字符串按主机顺序转换成数字
    压缩空格的函数以及BCD码与ASCII相互转换函数
    判断文件是否存在
    把指定长度字符串转换成数字
    找到特定串在源字符串中的位置
    FTP命令详解
    docker 学习路线
    云原生技术的了解
  • 原文地址:https://www.cnblogs.com/wt645631686/p/6868170.html
Copyright © 2020-2023  润新知