MySQL里,可以将多条SQL语句封装在一个存储程序里,这些SQL语句共同完成一项 功能,调用时只需调用这个存储程序,而不是书写若干条又臭又长的SQL语句。存储程序又可以分为存储例程、触发器和事件,存储例程又可以分为存储函数和存储过程。存储例程是需要使用者手动调用的,而触发器和事件是MySQL在特定条件下自动调用的,关系如下图:
1、存储函数
存储函数更贴近编程语言里函数的概念,有入参、返回值和函数体的概念,函数体由若干SQL语句和控制语句(比如循环语句、判断语句等)组成。
1.1 创建存储函数
格式:
CREATE FUNCTION 存储函数名称([参数列表])
RETURNS 返回值类型
BEGIN
函数体内容
END
举例:
delimiter $
# 存储函数,计算科目的平均分,入参为科目名称,返回值为平均分
CREATE FUNCTION avg_subject_score(s VARCHAR(30))
RETURNS DOUBLE
BEGIN
RETURN (SELECT AVG(score) FROM student_score WHERE s= subject);
END $
delimiter ;
注意:
- 是
RETURNS
而不是RETURN
; - 存储函数因为有返回值,因此函数体里一定要有
RETURN
语句; - 函数体里有多条
SQL
语句且用;分割开时;
需要用delimiter
重新定义语句结束分隔符,DataGrip
里可以不用; -
1.2 使用存储函数
格式:
存储函数名[(入参列表,逗号分隔)];
举例:
SELECT avg_subject_score('母猪的产后护理');
注意:
使用存储函数,就跟我们之前介绍的系统内置的函数是一个用法;
- 存储函数由于有且仅有一个返回值,因此存储函数可以用在查询列表、搜索条件和搜索表达式中。
1.3 查看和删除存储函数
1.3.1 查看存储函数
格式: ```sql查看当前数据库里所有存储函数
SHOW FUNCTION STATUS [LIKE 需要匹配的函数名];
查看指定的存储函数
SHOW CREATE FUNCTION 存储函数名;
<a name="xxkc8"></a>
### 1.3.2 删除存储函数
**格式:**
```sql
DROP FUNCTION 存储函数名;
1.4 函数体的定义
1.4.1 定义局部变量
之前介绍了自定义变量,这里介绍局部变量。自定义变量的作用范围是整个数据库的生命周期,局部变量的作用范围是存储函数的生命周期。
在函数体里使用局部变量,必须先用DECLARE
声明局部变量,再用SET
对局部变量赋值;相比于自定义变量,使用局部变量时,变量名前面不加@前缀。
声明局部变量:
格式:
DECLARE 变量名1, 变量名2, ... 数据类型 [DEFAULT 默认值];
举例:
CREATE FUNCTION test_function(val INT)
RETURNS INT
BEGIN
DECLARE local_val INT DEFAULT 0;
SET local_val = val * val;
RETURN local_val;
END;
注意:
- 使用局部变量前,必须先用
DECLARE
声明; - 使用局部变量时用
SET
,且局部变量前不需要用@前缀。1.4.2 使用自定义变量
存储函数的函数体里,不仅可以使用局部变量,还可以使用自定义变量,需要注意自定义变量使用时变量名前面有@前缀,局部变量没有。
举例: ```sql SET @global_val = 123;
CREATE FUNCTION test_function_2() RETURNS INT BEGIN DECLARE local_val INT DEFAULT 0; SET local_val = @global_val * 2; RETURN local_val; END;
<a name="gtmVq"></a>
### 1.4.3 存储函数的入参
**注意:**
- 存储函数的入参可以是一个,也可以是多个,多个入参间用逗号分隔;
- 存储函数的入参不可以和函数体语句中的其他变量名、列名等冲突;
- 调用函数的时候,必须显式的指定所有的参数,并且参数类型也一定要匹配。
<a name="iBBYt"></a>
# 2、存储过程
存储过程和存储函数都属于存储例程,同样可以将若干`SQL`语句封装在存储过程中供外部一次性调用。不同于存储函数,存储过程更倾向于单纯地批量执行`SQL`语句,因此存储过程里不需要`RETURN`返回值,存储过程中也可以一次性给多个变量赋值。
<a name="nqHQD"></a>
## 2.1 创建存储过程
**格式:**
```sql
CREATE PROCEDURE 存储过程名称([参数列表])
BEGIN
需要执行的语句
END
举例:
CREATE PROCEDURE test_procdure(id INT, name VARCHAR(30))
BEGIN
SELECT * FROM test_table;
INSERT INTO test_table VALUES (id, name);
SELECT * FROM test_table;
END;
2.2 使用存储过程
格式:
CALL 存储过程([参数列表]);
与调用存储函数不同,调用存储过程需要使用CALL
关键字。
举例:
CALL test_procdure(20180104, 'TIM');
2.3 查看和删除存储过程
2.3.1 查看存储过程
格式:
# 查看当前数据库中创建的存储过程
SHOW PROCEDURE STATUS [LIKE 需要匹配的存储过程名称]
# 查看某个存储过程
SHOW CREATE PROCEDURE 存储过程名称
2.3.2 删除存储过程
格式:
DROP PROCEDURE 存储过程名称
2.4 存储过程的参数前缀
存储过程相比存储函数,可以在入参名加参数前缀修饰,有三种参数前缀,如下:
前缀 | 实际参数是否必须是变量 | 描述 |
---|---|---|
IN |
否 | 用于调用者向存储过程传递数据,如果IN参数在过程中被修改,调用者不可见。 |
OUT |
是 | 用于把存储过程运行过程中产生的数据赋值给OUT参数,存储过程执行结束后,调用者可以访问到OUT参数。 |
INOUT |
是 | 综合IN 和 OUT 的特点,既可以用于调用者向存储过程传递数据,也可以用于存放存储过程中产生的数据以供调用者使用。 |
在DataGrip
中,存储例程(存储过程 + 存储函数)存放在与tables
目录并列的routines
目录下,如图:
2.5 存储函数和存储过程的不同点
存储函数和存储过程虽然都属于存储例程,但有以下不同点:
- 存储函数需要写
RETURNS
返回值类型,而且在函数体里要有RETURN
语句,而存储过程没有; - 存储函数只支持
IN
参数,而存储过程支持IN
参数、OUT
参数、和INOUT
参数; - 存储函数只能返回一个值,而存储过程可以通过设置多个
OUT
参数或者INOUT
参数来返回多个结果; - 存储函数执行过程中产生的结果集并不会被显示到客户端,而存储过程执行过程中产生的结果集会被显示到客户端;
- 存储函数直接在表达式中调用,而存储过程只能通过
CALL
语句来显式调用。3、控制语句
和其他高级编程语言一样,MySQL也有判断、循环等控制语句。
3.1 判断语句
格式:
IF 表达式 THEN
处理语句列表
ELSEIF 表达式 THEN
处理语句列表
... # 这里可以有多个ELSEIF语句
ELSE
处理语句列表
END IF;
举例:
CREATE FUNCTION test_function4(val INT)
RETURNS VARCHAR(30)
BEGIN
DECLARE result VARCHAR(30);
IF val > 60 THEN
SET result = '超过60';
ELSEIF val = 60 THEN
SET result = '等于60';
ELSEIF val < 60 THEN
SET result = '小于60';
ELSE
SET result = '非法输入';
END IF;
RETURN result;
END;
3.2 循环语句
MySQL有三种循环语句写法:
WHILE DO
;REPEAT
;LOOP
。
下面的例子都是用不同的循环语句格式计算前n个连续自然数之和。
3.2.1 WHILE DO
格式:
WHILE 表达式 DO
处理语句列表
END WHILE;
举例:
CREATE FUNCTION cal_function(val INT)
RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE i INT DEFAULT 1;
WHILE i <= val DO
SET result = result + i;
SET i = i + 1;
END WHILE;
RETURN result;
END;
3.2.2 REPEAT
格式:
REPEAT
处理语句列表
UNTIL 表达式 END REPEAT;
举例:
CREATE FUNCTION cal_function2(val INT)
RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE i INT DEFAULT 1;
REPEAT
SET result = result + i;
SET i = i + 1;
UNTIL i > val END REPEAT;
RETURN result;
END;
3.2.3 LOOP
格式:
LOOP
处理语句列表
END LOOP;
举例:
CREATE FUNCTION cal_function3(val INT)
RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE i INT DEFAULT 1;
LOOP
IF i > val THEN
RETURN result;
END IF;
SET result = result + i;
SET i = i + 1;
END LOOP;
END;
如果我们仅仅想结束循环break
,而不是使用RETURN
语句直接将函数返回,那么可以使用LEAVE
语句。不过使用LEAVE
时,需要先在LOOP
语句前边放置一个所谓的标记flag
,比方说我们使用LEAVE
语句再改写sum_all
函数:
CREATE FUNCTION cal_function4(val INT)
RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE i INT DEFAULT 1;
flag:LOOP
IF i > val THEN
LEAVE flag;
END IF;
SET result = result + i;
SET i = i + 1;
END LOOP flag;
RETURN result;
END
可以看到,我们在LOOP
语句前加了一个flag:
相当于为这个循环打了一个名叫flag
的标记,然后在对应的END LOOP
语句后边也把这个标记名flag
给写上了。在存储函数的函数体中使用LEAVE flag
语句来结束flag
这个标记所代表的循环。