02.存储过程和存储函数的输入输出

1,505 阅读4分钟

存储过程和存储函数的输入输出

定义一个方法,首先要定义的就是方法名以及入参出参。MySQL中有procedure(存储过程)和function(存储函数)两种类型的SQL语句集合

在Navicat中新建一个Function,Navicat会自动填一个模板

  CREATE DEFINER = CURRENT_USER PROCEDURE `NewProc`( )
  BEGIN
    #Routine body goes here...

  END;

这里DEFINER = CURRENT_USER是创建人,

后面PROCEDURE是存储过程,如果是函数的话就是FUNCTION

NewProc是存储过程名称,替换成自定义的名字

再往后的括号里面是参数。BEGIN, END之间就是写代码主体的部分了

本系列主要集中于使用实践,不详述完整的参数列表,CREATE的详细参数请参阅官方文档!

本篇主要介绍存储过程和存储函数参数部分的区别

1.存储函数

考虑一个计算两个数和的函数

  CREATE FUNCTION `simple_add_function`(a int, b int) RETURNS int(11)
  BEGIN
    return a + b;
  END

声明函数名为simple_add_function, 入参有两个,分别是a, b, 都是int类型的,该函数返回一个int类型的数字。 函数体里面return了a + b

可以看出,存储函数跟我们在其他编程语言里面用的函数没什么两样,就作为函数来理解就好了。

如果想在Navicat里面显式的调用一个函数

  select simple_add_function(1,2);

由于函数里面不能select,因此如果想拿到结果的话,需要自己在外面写个select。

或者赋值给一个变量再select出来

  set @temp = simple_add_function(1,2);
  select @temp;

为什么这里的变量是@开头的,会在下一节解释

2.存储过程

相对于函数,存储过程的输入输出就丰富了一些,有两进三出

  CREATE DEFINER=`cloudpenseuser`@`%` PROCEDURE `simple_add_procedure`(in a int, in b int, inout c int, out total int, out average int)
  BEGIN
    set total = a + b;
    set average = total / 2;
    if c > 0 then
      select 'ok';
    else
      select 'error';
      set c = 10;
    end if;
  END

储存过程的参数有in, inout, out三种类型,in, out分别是输入输出很容易理解,inout的参数既可以当输入,也可以当输出,而且在过程内部也可以select来取出数据

可以直接在Navicat或命令行中调用

 call simple_add_procedure(4, 2, 0, @a, @b);

考虑上面存储函数对应的Java实体类如下

  public class SimpleAddEntity {
    int a;
    int b;
    int c;
    int total;
    int average;

    //此处省略对应的getter和setter
  }

使用Mybatis的xml这么写

  <select id="simpleAdd" resultType="String">
    call simple_add_procedure(#{a}, #{b}, #{c,mode=INOUT, jdbcType=INTEGER}, #{total, mode=OUT, jdbcType=INTEGER, #{average, mode=OUT, jdbcType=INTEGER}})
  </select>

对应的Mybatis的interface

  public String simpleAdd(SimpleAddEntity simpleAddEntity);

通过mybatis来调用存储过程的时候,resultType是对应存储过程中select的内容,而存储过程out参数输出的内容会自动调用entity中的setter方法更新到对象中,或者用变量进行承接

上述示例的调用方法

  SimpleAddEntity simpleAddEntity = new SimpleAddEntity();
  simpleAddEntity.setA(4);
  simpleAddEntity.setB(2);
  String result = mapper.simpleAdd(simpleAddEntity)
  System.out.println(simpleAddEntity.getC()); //10
  System.out.println(simpleAddEntity.getTotal()); //6
  System.out.println(simpleAddEntity.getAverage()); //3
  System.out.println(result); //"error"

存储过程具有多种输出方式,开发中可以制定规范,统一使用某种输出方式。

例如可以使用out参数来传递状态和错误信息,通过select取返回数据

需要注意的是Mybatis只能取到第一个select的结果

相互调用

存储过程和函数可以互相调用,按逻辑上的输入输出即可

#获取函数的返回
declare ret varchar(255);
CREATE FUNCTION `function_A`(p_input int) RETURNS varchar(255) #function的声明,这里只是表示了一下,不能正常运行的
set ret = function_A(1); #在function_A中return
select ret;

#获取存储过程的返回
declare outparam varchar(255);
CREATE PROCEDURE `procedure_A`(in input int, out output varchar(255));
call procedure_A(input, outparam); #outparam是procedure_A的out或者inout参数
select outparam; #这里outparam就被赋值了

需要注意的是在存储过程select出来的结果不能由调用它的存储过程获取,会直接输出到最终结果的select

Tips: NodeJS如何取到out参数的返回值

笔者用过nodejs的MySQL包 github.com/mysqljs/mys…

使用的时候results只取select的结果的,这就导致了out返回的参数是没有体现在results中的,可以这么写

  connection.query(
    'set @x=""; set @y=""; set @z=""; call simple_add_procedure(?, ?, @x, @y, @z); select @x; select@y;  select@z',
    [4, 2],
    function (error, results, fields) {
      if (error) throw error
      let c = results[results.length - 3][0]['@x']
      let total = results[results.length - 2][0]['@y']
      let average = results[results.length - 1][0]['@z']
  });

写起来比较丑,如果你知道更好的写法,也希望你能教我