暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

MySQL之FUNCTION函数

GrowthDBA 2021-10-10
4041
上文我们介绍了视图(VIEW),本文我们介绍函数(FUNCTION)。在MySQL之DQL语句(四)中,我们介绍了很多如UPPER()等文本处理函数、DATE()等时间处理函数、ABS()等数据处理函数,上述这些函数是MySQL内置帮我封装好的,我们直接使用即可。

而我们今天介绍的函数(FUNCTION)是需要我们用户自己定义的,除了MySQL内置的函数外,我们想把处理某个问题的过程自己封装起来,也可以直接调用我们自定义的函数完成像MySQL内置函数的效果那样。今天要介绍的函数(FUNCTION)就是帮我们解决这个问题的利器。

本文摘录自小孩子4919《MySQL是怎样使用的:从零蛋开始学习MySQL》。

函数

创建函数

函数其实就是一种'函数',只不过在这个函数里可以执行MySQL的语句而已。它可以像MySQL内置函数一样把处理某个问题的过程封装起来,之后我们直接调用函数就可以去解决这个问题了。MySQL中定义函数的语句如下:

CREATE FUNCTION 函数名称([参数列表])RETURNS 返回值类型BEGIN    函数体内容END
定义一个函数需要指定函数名称、参数列表、返回值类型以及函数体内容。如果该函数不需要参数,那参数列表可以被省略,函数体内容可以包括一条或多条语句,每条语句都要以分号;结尾。下面就来创建一个函数:
DELIMITER $CREATE FUNCTION avg_price(s VARCHAR(100))RETURNS DOUBLEBEGIN  RETURN (SELECT AVG(prod_price) FROM products WHERE prod_name REGEXP s);END $DELIMITER ;

我们定义了一个名叫avg_price的函数,它接收一个VARCHAR(100)类型的参数,声明的返回值类型是DOUBLE,需要注意的是,我们在RETURN语句后边写了一个SELECT语句,表明这个函数的返回结果就是根据这个查询语句产生的,也就是返回了某类商品的平均价格。

小提示
定义函数首部位置的DELIMITER $是给MySQL解释器声明了一个结束符'$',END $表示定义的函数命令段结束了,最后的DELIMITER ;是又将结束符定义为MySQL默认的';'。
Q:为什么要这样做呢?
A:因为默认情况下,MySQL一遇到';',解释器就会认为命令输完了,要开始执行了。就像我们上面的例子,我们RETURN最后也有';',但是我们的函数命令还没有输完。所以,DELIMITER $就是为了避免这种情况的发生,事先将结束符换成非';'的其它符号,常见的一般换成'//'、'$$',然后再换回默认的';'。

函数的调用

函数定义好了,下面我们来看看如何调用。我们自定义的函数和系统内置函数的使用方式是一样的,都是在函数名后加小括号()表示函数调用,调用有参数的函数时可以把参数写到小括号里边。函数调用可以放到查询列表或者作为搜索条件,或者和别的操作数一起组成更复杂的表达式,我们现在来调用一下刚刚写好的这个名为avg_price的函数吧:

先来看下products表中的数据:

查看一下anvil、JetPack、TNT三类商品的平均价格:
SELECT avg_price('anvil');SELECT avg_price('JetPack');SELECT avg_price('TNT');

由此观之,通过调用函数的方式而不是直接写查询语句的方式来获取某类商品的平均价格看起来就简洁多了。

查看和删除函数

如果我们想查看我们已经定义了多少个函数,可以使用下边这个语句:

SHOW FUNCTION STATUS [LIKE 需要匹配的函数名]

如果我们想查看某个函数的具体是怎么定义的,可以使用这个语句:

SHOW CREATE FUNCTION 函数名

虽然展示出很多内容,但是我们只要聚焦于名叫Create Function的那部分信息,该部分信息展示了这个函数的定义语句是什么样的。

如果想删除某个函数,使用这个语句:

DROP FUNCTION 函数名

到现在为止我们只是勾勒出一个函数的大致轮廓,下边我们来详细说一下MySQL定义函数体时支持的一些语句。

函数体的定义

上边定义的avg_price的函数体里边只包含一条语句,如果只为了节省书写一条语句的时间而定义一个函数,其实也不是很值。其实函数的函数体中可以包含多条语句,并且支持一些特殊的语法来供我们使用,下边我们来一起看看。

在函数体中定义局部变量

函数中的函数体使用变量前必须先声明这个变量,声明方式如下:

DECLARE 变量名1, 变量名2, ... 数据类型 [DEFAULT 默认值];

这些在函数体内声明的变量只在该函数体内有用,当函数执行完成后,就不能访问到这些变量了,所以这些变量也被称为局部变量。我们可以在一条语句中声明多个相同数据类型的变量。

DELIMITER $$CREATE FUNCTION var_demo()RETURNS INTBEGIN  DECLARE c INT;  SET c = 5;  RETURN c;END $$DELIMITER ;

我们定义了一个名叫var_demo而且不需要参数的函数,然后在函数体中声明了一个名称为c的INT类型的局部变量,之后我们调用SET语句为这个局部变量赋值了整数5,并且把局部变量c当作函数结果返回。我们调用一下这个函数:

如果我们不对声明的局部变量赋值的话,它的默认值就是NULL,当然我们也可以通过DEFAULT子句来显式的指定局部变量的默认值,比如这样:
DELIMITER $$CREATE FUNCTION var_default_demo()RETURNS INTBEGIN  DECLARE c INT default 1;  RETURN c;END $$DELIMITER ;

在新创建的这个var_default_demo函数中,我们声明了一个局部变量c,并且指定了它的默认值为1,然后看一下该函数的调用结果:

得到的结果是1,说明了我们指定的局部变量默认值生效了!另外,特别需要注意一下我们可以将某个查询语句的结果赋值给局部变量的情况,比如我们改写一下前边的avg_price函数:
DELIMITER $CREATE FUNCTION avg_price(s VARCHAR(100))RETURNS DOUBLEBEGIN  DECLARE a DOUBLE;  SET a = (SELECT AVG(prod_price) FROM products WHERE prod_name REGEXP s);  RETURN a;END $DELIMITER ;

我们先把一个查询语句的结果赋值给了局部变量a,然后再返回了这个变量。

小提示
 在函数的函数体中,DECLARE语句必须放到其他语句的前边。

在函数体中使用自定义变量

除了局部变量外,也可以在函数体中使用自定义变量,比方说这样:

DELIMITER $$CREATE FUNCTION user_defined_var_demo()RETURNS INTBEGIN  SET @abc = 10;  RETURN @abc;END $$DELIMITER ;

我们定义了一个名叫user_defined_var_demo的函数,函数体内直接使用了自定义变量abc,我们来调用一下这个函数:

虽然现在函数执行完了,但是由于在该函数执行过程中为自定义变量abc赋值了,那么在该函数执行完之后我们仍然可以访问到该自定义变量的值,就像这样:

这一点和在函数体中使用DECLARE声明的局部变量有明显区别,大家注意一下。

函数的参数

在定义函数的时候,可以指定多个参数,每个参数都要指定对应的数据类型,就像这样:

参数名 数据类型

比如我们上边编写的这个avg_price函数:

这个函数只需要一个类型为VARCHAR(100)参数,我们这里给这个参数起的名称是s,不过这个参数名不要和函数体语句中的其他变量名、列名啥的冲突,比如上边的例子中如果把变量名s改为为prod_name,它就与下边用到WHERE子句中的列名冲突了。

另外,函数参数不可以指定默认值,我们在调用函数的时候,必须显式的指定所有的参数,并且参数类型也一定要匹配,比方说我们在调用函数avg_price时,必须指定我们要查询的商品名,不然会报错的:

判断语句的编写

像其他的编程语言一样,在函数的函数体里也可以使用判断的语句,语法格式如下:

IF 表达式 THEN    处理语句列表[ELSEIF 表达式 THEN    处理语句列表]... # 这里可以有多个ELSEIF语句[ELSE    处理语句列表]END IF;

其中处理语句列表中可以包含多条语句,每条语句以分号;结尾就好。我们举一个包含IF语句的函数的例子:

‍‍‍‍‍‍‍‍‍DELIMITER $$CREATE FUNCTION condition_demo(i INT)RETURNS VARCHAR(10)BEGIN  DECLARE result VARCHAR(10);  IF i = 1 THEN      SET result = '结果是1';  ELSEIF i = 2 THEN      SET result = '结果是2';  ELSEIF i = 3 THEN      SET result = '结果是3';  ELSE      SET result = '非法参数';  END IF;  RETURN result;END $$DELIMITER ;‍‍‍‍‍‍‍‍‍

在我们定义的函数condition_demo中,它接收一个INT类型的参数,这个函数的处理逻辑如下:

1、如果这个参数的值是1,就把result变量的值设置为'结果是1'。

2、否则如果这个这个参数的值是2,就把result变量的值设置为'结果是2'。

3、否则如果这个这个参数的值是3,就把result变量的值设置为'结果是3'。

4、否则就把result变量的值设置为'非法参数'。

我们现在调用一下这个函数:
SELECT condition_demo(1);SELECT condition_demo(2);SELECT condition_demo(5);

循环语句的编写

除了判断语句,MySQL还支持循环语句的编写,不过提供了3种形式的循环语句,我们一一道来:

  • WHILE循环语句
WHILE 表达式 DO    处理语句列表END WHILE;

这个语句的意思是:如果满足给定的表达式,则执行处理语句,否则退出循环。比如我们想定义一个计算从1到n这n个数的和(假设n大于0)的函数,可以这么写:

DELIMITER $$CREATE FUNCTION sum_all(n INT UNSIGNED)RETURNS INTBEGIN  DECLARE result INT DEFAULT 0;  DECLARE i INT DEFAULT 1;  WHILE i <= n DO      SET result = result + i;      SET i = i + 1;  END WHILE;  RETURN result;END $$DELIMITER ;

在函数sum_all中,我们接收一个INT UNSIGNED类型的参数,声明了两个INT类型的变量i和result。我们先测试一下这个函数:

分析一下这个结果是怎么产生的,初始的情况下result的值默认是0,i的值默认是1,给定的参数n的值是3。这个函数的运行过程就是:

1、先判断i <= n是否成立,也就是1 <= 3是否成立,显然成立,然后执行处理语句,将result的值设置为1(result + i = 0 + 1),i的值设置为2(i + 1 = 1 + 1)。

2、再判断i <= n是否成立,也就是2 <= 3是否成立,显然成立,然后执行处理语句,将result的值设置为3(result + i = 1 + 2),i的值设置为3(i + 1 = 2 + 1)。

3、再判断i <= n是否成立,也就是3 <= 3是否成立,显然成立,然后执行处理语句,将result的值设置为6(result + i = 3 + 3),i的值设置为4(i + 1 = 3 + 1)。

4、再判断i <= n是否成立,也就是4 <= 3是否成立,显然不成立,退出循环。

所以最后返回的result的值就是6,也就是1、2、3这三个数的和。

  • REPEAT循环语句

REPEAT循环语句和WHILE循环语句类似,只是形式上变了一下:
REPEAT    处理语句列表UNTIL 表达式 END REPEAT;

先执行处理语句,再判断表达式是否成立,如果成立则退出循环,否则继续执行处理语句。与WHILE循环语句不同的一点是:WHILE循环语句先判断表达式的值,再执行处理语句,REPEAT循环语句先执行处理语句,再判断表达式的值,所以至少执行一次处理语句,所以如果sum_all函数用REPEAT循环改写,可以写成这样:

DELIMITER $$CREATE FUNCTION sum_all(n INT UNSIGNED)RETURNS INTBEGIN  DECLARE result INT DEFAULT 0;  DECLARE i INT DEFAULT 1;  REPEAT      SET result = result + i;      SET i = i + 1;  UNTIL i > n END REPEAT;  RETURN result;END $$DELIMITER ;
  • LOOP循环语句

这只是另一种形式的循环语句:

LOOP    处理语句列表END LOOP;

不过这种循环语句有一点比较奇特,它没有判断循环终止的条件?那这个循环语句怎么停止下来呢?其实可以把循环终止的条件写到处理语句列表中然后使用RETURN语句直接让函数结束就可以达到停止循环的效果,比方说我们可以这样改写sum_all函数:

DELIMITER $$CREATE FUNCTION sum_all(n INT UNSIGNED)RETURNS INTBEGIN  DECLARE result INT DEFAULT 0;  DECLARE i INT DEFAULT 1;  LOOP      IF i > n THEN          RETURN result;      END IF;      SET result = result + i;      SET i = i + 1;  END LOOP;END $$DELIMITER ;

如果我们仅仅想结束循环,而不是使用RETURN语句直接将函数返回,那么可以使用LEAVE语句。不过使用LEAVE时,需要先在LOOP语句前边放置一个所谓的标记,比方说我们使用LEAVE语句再改写sum_all函数:

DELIMITER $$CREATE FUNCTION sum_all(n INT UNSIGNED)RETURNS INTBEGIN    DECLARE result INT DEFAULT 0;    DECLARE i INT DEFAULT 1;    flag:LOOP        IF i > n THEN            LEAVE flag;        END IF;        SET result = result + i;        SET i = i + 1;    END LOOP flag;    RETURN result;END $$DELIMITER ;

可以看到,我们在LOOP语句前加了一个flag:相当于为这个循环打了一个名叫flag的标记,然后在对应的END LOOP语句后边也把这个标记名flag给写上了。在函数的函数体中使用LEAVE flag语句来结束flag这个标记所代表的循环。

小提示
其实也可以在BEGIN ... END、REPEAT和WHILE这些语句上打标记,标记主要是为了在这些语句发生嵌套时可以跳到指定的语句中使用的。

小结

今天的实操内容较多,下面简单做一下总结:
1、函数其实就是一种'函数',在这个函数里可以执行MySQL的语句。它可以像MySQL内置函数一样把处理某个问题的过程封装起来,之后我们直接调用函数就可以去解决这个问题;
2、定义一个函数需要指定函数名称、参数列表、返回值类型以及函数体内容。如果该函数不需要参数,那参数列表可以被省略,函数体内容可以包括一条或多条语句,每条语句都要以分号;结尾;
3、函数的创建、调用、查看和删除语法;
4、局部变量:在函数体内声明的变量只在该函数体内有用,当函数执行完成后,就不能访问到这些变量了,所以这些变量也被称为局部变量。局部变量和自定义变量的区别:自定义变量的值在函数执行完之后我们仍然可以访问到。函数参数不可以指定默认值,我们在调用函数的时候,必须显式的指定所有的参数,并且参数类型也一定要匹配;
5、函数的判断语句编写、三种循环语句的编写:WHILE、REPEAT、LOOP,三种循环方式的区别。
今天主要学习了MySQL的FUNCTION函数,实操的内容比较多,大家下来多加练习,函数这部分内容是比较重要的,一定要掌握。每天进步一点点。

end

文章转载自 GrowthDBA,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论