• 存储过程
  • 存储函数
  • Mysql 存储过程

    之前一直对mysql的存储过程不了解,总感觉哪里不对劲,所以有了这篇博客,这是这一序列的第二篇。。事实上这是对mysql存储过程的一个基本认识,里面还有好多疑问需要解决。另外那些标榜从入门到精通的书大多不够精通,个人认为一门技术很难靠一本书从入门到精通的,即使有写得这样全面的书,也很可能达到的效果变成了“从入门到放弃”。当然我并不是说我参考的书写得不好,相反,在这里我学习到了很多,很多mysql的书甚至连视图,存储过程都没有。后面可能还有函数,触发器。

    存储过程

    创建存储过程

    每个参数有三部分组成:输入输出类型,参数名称,参数类型

    create procedure procedure_name;
    in 输入参数
    out 输出参数
    inout 输入/输出
    DECLARE 过程体中声明变量,只能在begin,end之间声明,且在开始就声明,即声明再使用
    set 赋值 也可以用select into,select name into name from test where id =2; name 需要先定义
    
    delimeter 设置结束符,默认为;
    delimiter ##
    CREATE PROCEDURE del_pro(IN t_id INT)
    BEGIN
        DELETE FROM test WHERE id = t_id;
    END ##
    delimiter ;
    
    delimiter ##
    create procedure age_from_test(in name char(4),out ages int)
    begin
      select age into ages
      from test
      where name = name;
    end ##
    delimiter ;
    

    查看:

    MariaDB [oldboy]> show procedure status \G
    *************************** 1. row ***************************
                      Db: mysql
                    Name: AddGeometryColumn
                    Type: PROCEDURE
                 Definer: root@localhost
                Modified: 2019-12-19 11:20:15
                 Created: 2019-12-19 11:20:15
           Security_type: INVOKER
                 Comment:
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: latin1_swedish_ci
    *************************** 2. row ***************************
                      Db: mysql
                    Name: DropGeometryColumn
                    Type: PROCEDURE
                 Definer: root@localhost
                Modified: 2019-12-19 11:20:15
                 Created: 2019-12-19 11:20:15
           Security_type: INVOKER
                 Comment:
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: latin1_swedish_ci
    *************************** 3. row ***************************
                      Db: oldboy
                    Name: del_pro
                    Type: PROCEDURE
                 Definer: root@%
                Modified: 2020-03-26 21:10:45
                 Created: 2020-03-26 21:10:45
           Security_type: DEFINER
                 Comment:
    character_set_client: utf8mb4
    collation_connection: utf8mb4_general_ci
      Database Collation: latin1_swedish_ci
    3 rows in set (0.00 sec)
    

    带参数存储过程

    delimiter //
    create procedure get_count(out ct int)
    begin
     select count(1) into ct from test;
    end //
    delimiter ;
    
    MariaDB [oldboy]> delimiter ;
    MariaDB [oldboy]> call get_count(@ct);
    Query OK, 1 row affected (0.00 sec)
    
    MariaDB [oldboy]> select @ct;
    +------+
    | @ct  |
    +------+
    |    5 |
    +------+
    1 row in set (0.00 sec)
    
    delimiter //
    create procedure get_age(in id int, out _age int)
    begin
      select age into _age
      from test
      where id = id;
    end //
    delimiter ;
    
    delimiter //
    create procedure get_age(in _id int, out _age int)
    begin
      select age into _age
      from test
      where id = _id;
    end //
    delimiter ;
    
    @ 注意参数名不能与列名相同,否则查不到数据
    MariaDB [oldboy]> delimiter ;
    MariaDB [oldboy]> call get_age(6,@_age);
    Query OK, 1 row affected (0.00 sec)
    
    MariaDB [oldboy]> select @_age;
    +-------+
    | @_age |
    +-------+
    |    18 |
    +-------+
    1 row in set (0.00 sec)
    

    在执行上面的存储过程中出现 两个错误:

    MariaDB [oldboy]> call get_age(1,_age);
    ERROR 1414 (42000): OUT or INOUT argument 2 for routine oldboy.get_age is not a variable or NEW pseudo-variable in BEFORE trigger
    #错误原因是调用的参数_age 前面没有加@
    
    MariaDB [oldboy]> call get_age(1,@_age);
    ERROR 1172 (42000): Result consisted of more than one row
    错误原因存储过程中的where id = id; 参数名与列名相同 修改为where id = _id正常
    
    流程控制 if
    delimiter //
    create procedure check_age(in _id int, out is_adult int)
    begin
      declare _age int;
      select age into _age
      from test
      where id = _id;
      if _age >18 then
        set is_adult=1;
      elseif _age < 18 then
        set is_adult =0;
       else
         set is_adult =1;
       end if;
    end //
    delimiter ;
    
    MariaDB [oldboy]> delimiter ;
    MariaDB [oldboy]> call check_age(1,@is_adult);
    Query OK, 1 row affected (0.00 sec)
    
    MariaDB [oldboy]> select @is_adult;
    +-----------+
    | @is_adult |
    +-----------+
    |         1 |
    +-----------+
    1 row in set (0.00 sec)
    #这里is_adult=1表示是成年,但上面的存储过程有个bug, else中的 is_adult=1,导致除了小于18的都返回1,另外declare 语句要在begin,end之间,且要加上分号(;)否则会报语法错误
    
    流程控制 case
    delimiter //
    create procedure get_gender_count(in _gender char(6), out gender_count int)
    begin
      case _gender
        when 'male' then select count(name) into gender_count 
          from test where gender='male';
        when 'female' then select count(name) into gender_count 
          from test where gender='female';
        else select count(1) into gender_count from test;
      end case;
    end //
    delimiter ;
    
    MariaDB [oldboy]> call get_gender_count('female',@gender_count);
    Query OK, 1 row affected (0.00 sec)
    
    MariaDB [oldboy]> select @gender_count;
    +---------------+
    | @gender_count |
    +---------------+
    |             3 |
    +---------------+
    1 row in set (0.00 sec)
    
    MariaDB [oldboy]> call get_gender_count('male',@gender_count);
    Query OK, 1 row affected (0.00 sec)
    
    MariaDB [oldboy]> select @gender_count;
    +---------------+
    | @gender_count |
    +---------------+
    |             2 |
    +---------------+
    1 row in set (0.00 sec)
    
    MariaDB [oldboy]> call get_gender_count('fm',@gender_count);
    Query OK, 1 row affected (0.00 sec)
    
    MariaDB [oldboy]> select @gender_count;
    +---------------+
    | @gender_count |
    +---------------+
    |             5 |
    +---------------+
    1 row in set (0.00 sec)
    
    循环语句 while
    delimiter //
    create procedure dowhile(inout v int)
    begin
      while @v >0 do
          set @v = @v-1;
      end while;
    end //
    delimiter ;
    

    这里有个问题,这种情况怎么传参数进去呢?call dowhile(4)语法报错,而且不知道怎么获取存储过程的值,此坑待填。

    循环语句loop

    label是标志,可以省略不写 leave 跳出循环

    delimiter //
    create procedure doloop()
    begin
        set @a =10
        lable:loop
        set @a = @-1;
        if @a<0 then
        leave label;
        end if;
        end loop label;
    end //
    delimiter ;
    
    循环语句 while
    delimiter //
    create procedure dorepeat()
    begin
      repeat
      v = v-1
      until v <1
      end repeat;
    end //
    delimiter ;
    
    再次循环iterate
    delimiter //
    create procedure doiterate()
    begin
        declare p int default 0;
        my_loop: Loop
            set p = p +1;
            if p<10 then iterate my_loop;
            elseif p >20 then leave my_loop;
            end if;
            select 'p is between 10 and 20';
        end loop my_loop;
    end//
    delimiter ;
    
    #执行后会多次打印下面的内容
    MariaDB [oldboy]> call doiterate();
    +------------------------+
    | p is between 10 and 20 |
    +------------------------+
    | p is between 10 and 20 |
    +------------------------+
    1 row in set (0.00 sec)
    
    修改存储过程
    delimite //
    CREATE PROCEDURE num_from_student(IN _birth DATE,OUT count_num INT)
    READS SQL DATA
    BEGIN
        SELECT COUNT(*) INTO count_num
        FROM student
        WHERE sbirthday=_birth;
    END//
    delimiter ;
    
    alter procedure dowhnum_from_studentile
    modifiles sql data  # 将读写权限修改为modifiels sql data, 并指名调用者可以执行
    sql security inviker;
    
    删除存储过程
    drop procedure if exists age_from_test;
    MariaDB [oldboy]> drop procedure if exists age_from_test;
    Query OK, 0 rows affected (0.00 sec)
    

    存储函数

    存储过程中的语句在存储函数中都可以用,但不能指定in,out,inout,且必须有返回return,当return语句中包含select时,返回结果只能有一行

    创建存储函数
    delimiter //
    create function get_name(_id int)
    returns char(6)
    begin
        return (select name from test where id=_id);
    end//
    delimiter ;
    
    # 当添加in,out 等关键字时会报错,但mysql5.7从入门到精通书里竟然可以加,我实践后报错,然后help create function udf 提示的也没有这个参数。
    ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'in _id int)
    
    调用存储函数
    select get_name(1);
    
    MariaDB [oldboy]> select get_name(1);
    +-------------+
    | get_name(1) |
    +-------------+
    | jack        |
    +-------------+
    1 row in set (0.00 sec)
    
    查看存储函数
    MariaDB [oldboy]> show function status like 'get_name' \G
    *************************** 1. row ***************************
                      Db: oldboy
                    Name: get_name
                    Type: FUNCTION
                 Definer: root@%
                Modified: 2020-03-27 17:40:12
                 Created: 2020-03-27 17:40:12
           Security_type: DEFINER
                 Comment:
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: latin1_swedish_ci
    1 row in set (0.00 sec)
    
    修改存储函数
    alter function get_name from oldboy
    reads sql data
    comment  'get name';
    
    删除存储函数
    drop function function_name;
    

    ref: 《mysql网络数据库开发》 《mysql5.7 从入门到精通》

    上一篇:Mysql 视图

    下一篇:Mysql 触发器