MySQL那些你搞不清楚的存储程序,来这里听我一一道来

87 阅读21分钟

MySQL基础(八)-----视图和存储程序

视图是什么?视图是一种虚拟表;视图就是帮助我们以很容易的方式来复用一些sql语句

存储程序,存储程序可以封装一些语句,然后为用户提供一种简单的方式来调用这个存储程序,从而间接的执行这些语句。

根据调用方式的不同,可以把存储程序分为存储例程、触发器和事件这几种类型。其中存储例程又可以被细分为存储函数和存储过程。

一、视图

引入视图前有必要说明一下,视图目前已经很少用了,所以大家做一个简单的了解就行了,知道他是啥,最基础的操作了解一下即可。

MySQL推荐用视图,可以提高查询性能,MySQL又不提倡视图,这到底是为什么?

问题一:为什么MYSQL很少人用视图?

        答:mysql并不是很少人用,而是大部分一般直接通过sql查询的方式来实现类似view功能,不愿意 去增加这么一个过程,其实增加view还是有很多好处:

  • 简单性。视图不仅可以简化用户对数据的理解,也可以简化他们的操作。
  • 安全性。通过视图用户只能查询和修改他们所能见到的数据。
  • 逻辑数据独立性。视图可以使应用程序和数据库表在一定程度上独立。 

问题二:MySQL和MSSQL有哪些本质区别? 

        答:MySQL可以说是MSSQL的简化版本。理念相同,但MySQL的实现比MSSQL的需求低。MySQL是一个免费的、开放源代码的SQL数据库,所以免费的MYSQL很受欢迎。

参考链接

1、创建视图

创建视图前我们先了解一下原来的查询语句

SELECT s1.id,s1.`name`,s1.major,s2.`subject`,s2.score FROM student s1 INNER JOIN student_score s2 where s1.id = s2.number AND s1.department='计算机学院';

可以发现这个sql又臭又长,如果每次都敲一遍的话,很繁琐,所以这个时候我们就用到了视图。

先看视图的基本语句:

create view 视图名 as 查询语句;

创建视图样例:

CREATE VIEW male_student_view AS SELECT s1.id,s1.`name`,s1.major,s2.`subject`,s2.score FROM student s1 INNER JOIN student_score s2 where s1.id = s2.number AND s1.department='计算机学院';

这样我们就把上面又臭又长的sql用视图来表示了

2、使用视图

视图也可以称为虚拟表,原因是因为我们可以像对表那样对视图进行一些增删改查,只不过对视图的相关操作都会被映射到后面的查询语句对应的底层的表上。那串又臭又长的查询语句的查询列表可以被当作视图的虚拟列。

其实我们查询视图和查询表的sql是一样的,只不过我们把表名改成了视图名

模版

select * from 视图名;

样例:

SELECT * FROM male_student_view;

 

其他的操作,大家可以自己写着试试一下,有问题,可以在 评论区讨论交流。 

        再次强调一遍,视图其实就相当于某个查询语句的别名!创建视图的时候并不会把那个又臭又长的查询语句的结果集维护在硬盘或者内存中!在对视图进行查询时,mysql服务器会帮助我们对视图的查询语句转换为底层表的查询语句,然后在执行。

        虽然视图的实现原理是在执行语句时转换为对底层表的操作,但是在使用层面,我们完全可以把视图当做一个表去使用。

使用视图的好处:

  • 视图可以简化语句的书写,避免了每次都要写一遍又臭又长的语句;
  • 视图的操作更加直观,使用者也不用去考虑它的底层实现细节

2.1、利用视图来创建新视图

用于生成视图的查询语句,不仅仅可以从真实表中查询数据,也可以从另一个已生成的视图中查询数据。

样例

CREATE VIEW male_student_view2 as SELECT id,name,score FROM male_student_view;

2.2、创建视图时指定自定义别名

我们现在已经知道视图的虚拟列其实是这个视图对应的查询语句的查询列表。我们可以在创建视图的时候为它的虚拟列自定义列名,这些自定义列名需要用小括号括起来,写到视图名后面,并使用逗号将各个虚拟列的自定义列名分开。

注意:自定义列名一定要与查询列表中的表达式一一对应。

模版:

CREATE VIEW 视图名(no,n,m) as 查询语句;
-- --查询语句中返回字段要和视图名的列名一一对应

样例:

CREATE view student_info_view(no,n,m) as SELECT id,name,major FROM student;

 

在定义视图时,我们采用了自定义列名后,那么后面的查询该视图的时候就不能使用原查询语句查询列表中的列名了。

样例

3、查看和删除视图

3.1、查看有哪些视图

我们创建视图时,默认是将其放在默认数据库下的。如果想查看默认数据库下的视图。如下所示,和查表的语句一样的

样例:

SHOW TABLES;)

注意⚠️因为视图是虚拟表,所以新创建的视图的名称不能和默认数据库中的其他视图或者表的名称冲突!

3.2、查看视图的定义

查看视图结构的语句与用来查看表结构的语句比较类型。

模版:

SHOW CREATE VIEW 视图名\G;

样例:

SHOW CREATE VIEW student_info_view\G;

注意:我们查询出来的视图结构中多了很多信息,比如说ALGORITHM=UNDEFINED、DEFINED=root@%、sql security definer等,这些信息暂时不需要关心,跳过即可。

4、可更新的视图

前三个小节讲的都是如何对视图进行查询操作,其实有的视图是可以更新的,也就是可以在视图上执行insert、delete、update语句。对视图执行insert、delete、update语句的本质,其实就是对该视图对应的底层表中的数据进行增删改操作。

样例:

UPDATE student_info_view SET n='哈撒给' WHERE no =20230101;
SELECT name FROM student WHERE id=20230101;
/**
 name
哈撒给
**/

如果一个视图是可以更新的,那么这个视图中的每一条记录必须与底层表中的每一条记录一一对应,否则视图是不可更新的。

注意⚠️包含以下内容生成视图的查询语句不可以更新

  • 汇总函数(比如sum、min、max、count等);
  • distinct;
  • group by;
  • having;
  • union或者union all;
  • 放在查询列表中的子查询;
  • ...

一般只在查询语句中使用视图,并不建议在insert、delete、update上使用视图。

4.1、删除视图

某个视图不要了,可以直接删除

模版:

DROP VIEW 视图名;

样例:

DROP VIEW male_student_view2;
SHOW TABLES;

二、存储程序

存储程序分为存储例程、触发器和事件这几种类型。其中存储例程又可以被细分为存储函数和存储过程。

看到这些,小伙伴们不要慌,我们来逐个击破。

1、用户自定义变量  

在日常生活中,我们把遇到的一些固定不变的值,称为常量;把可以变化的值称为变量,比如用符号代表一个值,这个值是可以发生变化的。

我们可以通过set语句来自定义一些自己的变量

样例:

SET @a = 1;

这个地方,我们定义了一个称为a的变量,并给这个变量赋予了1这个值。

这个地方我们需要注意下@符号,在我们使用set设置自定义变量的时候,在变量前面必须加一个@符号,这是MySQL的规定。

在使用set语句时,如果变量名前没有设置@符号,那么MySQL会把这个变量当作系统变量来对待,而不是用户自定义变量。

如果我们想要查询这个变量的值的话, 我们直接使用select查询语句即可。不过在查询的时候也需要在变量名前面加上@符号。

select @a;

 另外同一个变量也可以存储不同类型的值。我们还可以把a变量的值改为字符串

set @a='哈撒给';
select @a;

我们还可以把一个变量赋值给另一个变量

set @b = @a;
select @b;

这个地方就是我们先把a的值赋值给变量b,这个时候变量a和b的值都是“哈撒给”,后面如果我们修改了变量a的值,变量b的值也不会变得。大家可以自己操作查看一下效果。

除了上面这些,我们还可以将某个查询的结果复制给另一个变量,前提是这个查询的结果集最多包含1行1列(如果结果集为空集,则变量值为null)

这个地方有两种写法,我都写出来:

set @a=(select a1 from t1 limit 1);
/**这个地方还可以使用into子句替代set语句来为变量赋值**/
select b1 from t1 limit 1 into @b;

当某个查询语句的结果集最多包含一条记录,但是结果集的记录中包含多个列时,如果我们想将集记录中各列的值分别赋值给不同的变量,此时就不能使用set语句了,而只能用into子句来完成这个功能。

select a1,b1 from t1 limit 1 into @a,@b;
/**a1列的值赋给了a,b1列的值赋给了b**/

2、存储函数

之前说到,可以将某个常用的功能对应的一些语句封装成一个所谓的存储程序,之后只要调用这个存储程序就可以完成这个常用功能,从而免去了每次都要写好多语句的麻烦。

存储程序可以分为存储例程,触发器,事件这几种类型。其中存储例程需要我们去手动调用,而触发器和事件都是MySQL服务器在特定条件下自己调用的。存储例程又可以分为存储函数和存储过程。

2.1、创建存储函数

函数可以将某个的处理过程封装起来,之后直接调用就可以解决这个问题,而不用关心这个问题具体是怎么解决的。之前有很多函数,我这里就不一一列举了,大家可以去专栏里面找相应的文章去查看。

存储函数也是一种函数,只不过是在函数定义中书写MySQL的语句而已。

模板:

CREATE FUNCTION 存储函数名称([参数列表])
RETURNS 返回值类型
BEGIN 
	函数体内容
END

从SQL进行分析,定义一个存储函数时,需要指定函数名称、参数列表、返回值类型以及函数体内容。如果该函数不需要参数,则参数列表可以省略,函数体内容被包裹在begin...end中,可以包括一条或多条语句,没叫语句要以分号(;)结尾。

样例:

CREATE FUNCTION avg_score(s VARCHAR(100))
RETURNS DOUBLE
BEGIN
	RETURN (SELECT avg(score) FROM student_score WHERE subject = s);
END

定义的这个存储函数,返回值类型是DOUBLE类型,所以return后面查询语句返回的值要是DOUBLE类型。

这个SQL如果直接放在客户端执行的话,会报错。报错的原因大概就是,语法错误,因为MySQL客户端默认将分号(;)作为语句的分隔符,每当它读取的时候,遇到分号后就会将该分号前的内容作为一个请求发送给MySQL服务器。

所以上面这个样例被服务器接收到的实际上没有end哪一行。

我们要想解决这个问题,只需要使用delimiter语句临时修改一下MySQL客户端的语句分隔符就好。样例:

delimiter $;#将MySQL客户端语句分隔符替换为$

修改一下我们的SQL,再次放入到客户端执行即可了 

delimiter $
CREATE FUNCTION avg_score(s VARCHAR(100))
RETURNS DOUBLE
BEGIN
	RETURN (SELECT avg(score) FROM student_score WHERE subject = s);
END $

其中delimiter 意味着将MySQL客户端的语句分隔符从;替换成意味着将MySQL客户端的语句分隔符从;替换成。在存储函数写完之后,加上一个$,以表示书写完毕,提交服务器请求。

记住!!!操作完之后,一定要把分隔符再改回来

如果在创建存储函数的过程中报一下错误:

error 1418(HY000):this function has none of deterministic,no sql,or reads sql data in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)

则可以先执行一下面的语句,然后再尝试创建存储函数:

set global log_bin_trust_function_creators=ture;

2.2、存储函数的调用

自定义函数和系统内置函数的使用方式是一样的,都是在函数名后加小括号()来表示函数调用;在调用有参数的函数时,可以把参数写到小括号里面。函数调用可以单独使用,也可以作为一个操作数与其他操作数一起组成更复杂的表达式。

我们经常性的把函数调用放在查询列表或者作为搜索条件使用。

select avg_score('MySQL技术');
select avg_score('计算机科学与技术');

2.3、查看和删除存储函数

查看存储函数的SQL:

查看现在已经定义了多少个存储函数以及各个存储函数的相关属性

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

如果不写like语句,则执行该语句的结果会有很多,因为系统中自带了许多存储函数。这里就不做具体的演示了,演示就是刷屏,大家可以自己去玩一下。

查看某个函数具体是怎么定义的

show create function 函数名

删除某个存储函数

drop function 函数名

2.4、函数体的定义

前面我们介绍的自定义函数里面的函数体只包含了一条语句。其实存储函数的函数体中,是可以包含多条语句的,并且支持一些特殊的语法。主要分为五种。

1、在函数体中定义局部变量

在介绍用户自定义变量时说过,可以直接使用set语句为自定义变量赋值而不用事先声明它。在存储函数的函数体中也想使用变量的话,我们提前使用DECLARE语句声明该变量。

模板:

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

在这些函数体内声明的变量只在该函数体内有用,当存储函数执行完成后,就不能访问这些变量了,所以这些变量也被称为局部变量。

同时,在一条语句中,我们可以声明多个相同数据类型的变量。不过需要注意的是,函数体中的额局部变量名不允许加@前缀(除非是使用反引号将变量名引起来),这一点与之前直接使用set语句自定义变量是不同的,小伙伴们需要注意一下。

样例

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

我们调用上面这个函数值的返回值就是5.实现原理,就是我们先定义了一个不需要参数的存储函数,然后在函数体中声明了一个名称为c的int类型的局部变量,之后调用set语句进行赋值为5,最后把这个局部变量c作为函数结果返回。

如果不对声明的局部变量进行赋值,它的默认值是NULL,当然我们也可以使用default进行显示的指定局部变量的默认值。

delimiter $
CREATE FUNCTION var_demo()
RETURNS INT
BEGIN
	DECLARE c INT DEFAULT 2;
	RETURN c;
END $

这个是再调用这个函数的返回值就是2了。另外与用户自定义变量类似,我们也可以把一个查询的结果赋值给局部变量。大家可以想一下sql怎么写,可以在评论区交流一下。这里就不作展示了。

在存储函数的函数体中,declare语句必须放到其他的语句的前面。

下面还是把上面没展示的SQL做一个演示吧,原理大家自己分析理解:

delimiter $
CREATE FUNCTION var_demo()
RETURNS DOUBLE
BEGIN
	DECLARE c DOUBLE;
	SET c=(SELECT avg(score) FROM student_score WHERE subject = s);
	RETURN c;
END $

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

除了局部变量外,也可以在函数体中使用之前用过的用户自定义变量。

delimiter $
CREATE FUNCTION use_defined_var_demo()
RETURNS INT
BEGIN
	SET @ab=9RETURN @ab;
END $

/**
1、先查询@ab,会发现结果为null
2、执行函数后,返回值为9;
3、在查询@ab,结果为9
**/

值的变化,意味着即使使用存储函数执行完毕,该存储函数修改过的用户自定义变量的值依旧继续生效。这个和局部变量的declare有区别,请小伙伴们注意下。

3、存储函数的参数

在定义存储函数的时候,可以指定多个参数,且每个参数都要指定对应的数据类型。

参数名 数据类型

可以拿我们之前定义的avg_score函数为例,其中里面的s的类型是为varchar(100)的参数,注意的是这个参数名不要与函数体语句中的其他变量名、列名冲突;函数参数不可以指定默认值;在调用有参数的函数时,必须要带上参数,否则会报错!

4、判断语句的编写

存储函数中函数体的判断语句格式如下:

IF 表达式 THEN
	语句表达式
[ELSEIF 表达式 THEN
	语句列表]
...# 这里可以有多个elseif语句
[ELSE
	语句列表]
END 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 $
  • 如果i=1,那么函数返回结果为1;
  • 如果i=2,那么函数返回结果为2;
  • 如果i=3,那么函数返回结果为3;
  • 以上三个条件都不满足,那函数返回结果就是非法参数。

其他的场景大家可以自己模拟操作一下。

5、循环语句的编写

循环语句主要分为三种:while、repeat、loop。

  • while循环语句:表达式不成立是语句不执行(先判断后执行)
while 表达式 do
    语句列表
end while

样例:

delimiter $
CREATE FUNCTION while_demo(n INT UNSIGNED)
RETURNS INT
BEGIN
	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 $

/**计算1到n的值**/
  • repeat 循环语句:表达式不成立,语句也至少执行一次(先执行后判断)
repeat
    语句列表
until 表达式 end repeat;

样例:

CREATE FUNCTION repeat_demo(n INT UNSIGNED)
RETURNS INT
BEGIN
	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
/**计算1到n的值**/
  • loop循环语句:没有判断循环终止的条件
loop
    语句列表
end loop;

loop循环:

  • 可以把循环终止的条件写到语句列表中,然后使用return语句直接让函数结束,从而实现终止循环的效果:
CREATE FUNCTION loop_demo(n INT UNSIGNED)
RETURNS INT
BEGIN
	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
/**计算1到n的值**/
  • 如果我们仅仅想要结束循环,而不是使用return语句直接将函数返回,那么可以使用leave语句。不过在使用leave时,需要先在loop语句前面放置一个所谓的标记。就是flag,标记名字大家可以自取。
CREATE FUNCTION loop_leave_demo(n INT UNSIGNED)
RETURNS INT
BEGIN
	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 $

我们在loop语句前面加了一个flag的标记,并在end loop语句后面也写了一个flag单词,相当于就是给整个循环语句打了一个名为flag的标记。然后我们在循环内部加了一个 leave flag的语句,这样当我们执行到leave flag语句的时候,我们就会结束flag所代表的的循环。

其实也可以在begin...end。repeat和while这些语句上打标记。标记主要是为了这些语句发生嵌套时可以跳到指定的语句。

3、存储过程

存储函数和存储过程都是属于存储例程,都是对某些语句的一个封装。存储函数会给调用它的用户返回一个结果,但是存储过程是没有返回值的。

3.1、创建存储过程

创建存储过程的SQL:

CREATE PROCEDURE 存储过程名称([参数列表])
BEGIN
	需要执行的语句
END

存储过程的定义是不需要声明返回值类型的。

样例:

delimiter $
CREATE PROCEDURE procedure_demo(
	a1_value INT,
	b1_value char(1)
)
BEGIN
	SELECT * FROM t1;
	INSERT INTO t1(a1,b1) VALUES(a1_value,b1_value);
	SELECT * FROM t1;
END $
delimiter ;

上面创建了一个demo,定义了两个参数,做了三件事,先查询t1表一次,然后往t1表里插入数据,再查一下t1表。

3.2、存储过程的调用

存储过程没有返回值,不能像存储函数那样进行函数调用。如果我们需要调用某个存储过程,需要显式地使用call语句

call 存储过程([参数列表]);

只有查询语句才会产生结果集,其他语句时不产生结果集的。

3.3、查看和删除存储过程

用于查看目前服务器上已经创建了那些存储过程的语句如下所示:

show procedure status [like 需要匹配的存储过程名称];

 用于查看某个存储过程具体是怎么定义的语句如下所示:

show create procedure 存储过程名称

用于删除存储过程的语句如下所示:

drop procedure 存储过程名称

3.4、存储过程中的语句

这个地方不做多的解释,和存储函数时使用到的各种语句一样。

3.5、存储过程的参数前缀

比存储函数强大一点的是,存储过程在定义参数的时候可以选择添加一些前缀。

[IN | OUT | INOUT] 参数名 数据类型

  • IN参数:in参数只能用于读取,对它赋值是不会被调用者看到的。
delimiter $
CREATE PROCEDURE in_demo(
	IN arg INT
)
BEGIN
	SELECT arg;
	SET arg = 123;
END $
delimiter ;


/**
set @a = 1;

call in_demo(@a);#1

select @a;#1
**/
  • OUT参数:只能用于赋值,对它赋值是可以被调用者看到的。(由于out参数只是为了用于将存储过程的执行过程中产生的数据赋值给它后交给调用者看,因此在调用存储过程时,实际的参数就不允许是常量!)
delimiter $
CREATE PROCEDURE out_demo(
	OUT arg INT
)
BEGIN
	SELECT arg;
	SET arg = 123;
END $
delimiter ;


/**
set @b = 1;

call out_demo(@b);#NULL

select @b;#123
**/
  • INOUT参数:既可以在存储过程中被录取,也可以在赋值后被调用者看到(所以要求在调用存储过程时,实际的参数必须是一个常量,不然怎么赋值呢)---这里就不展示了,大家可以自己操作一下,评论区讨论

注意:如果不写明参数前缀的话,则默认前缀是IN!

存储过程可以传入多个out或者INOUT类型的参数,所以可以在一个存储过程中获得多个结果

delimiter $
CREATE PROCEDURE get_datas_demo(
	OUT max_score DOUBLE,
	OUT min_score DOUBLE,
	OUT avg_score DOUBLE,
	s VARCHAR(100)
)
BEGIN
	SELECT MAX(scroe),MIN(score),AVG(score) FROM student_score WHERE `subject` = s INTO max_score,min_score,avg_score;
END $
delimiter ;

3.6、存储过程和存储函数的不同点

  • 存储函数在定义时需要显式使用returns语句标明返回的数据类型,而且在函数体中必须使用return语句来显式指定返回值;存储过程不需要。
  • 存储函数不支持IN、OUT、INOUT的参数前缀;存储过程支持。
  • 存储函数只能返回一个值;存储过程可以通过设置多个OUT参数或者多个INOUT参数来返回多个结果。
  • 存储函数在执行过程中产生的结果集并不会被显示到客户端;存储过程在执行过程中产生的结果集会被显示到客户端。
  • 存储函数直接以函数调用的形式进行调用;存储过程只能通过call语句来显式调用。

4、游标简介

到目前的介绍为止,我们只能使用select...into...语句将结果集中一条记录的各个列的值赋值到多个变量中。

如果某个查询语句的结果集中有多条记录的话,按照现在的介绍,我们无法给他们进行赋值某些变量了。这个时候我们就需要用到即将介绍的游标(cursor)了。

正常我们的查询语句,是从第一条开始查到最后一条数据。而游标就是用来标记结果集中我们正在访问的某一条记录。在初始状态下,游标标记结果集中的第一条记录。我们可以根据这个游标取出对应记录的信息,随后在移动游标,让它指向下一条记录。

游标既可以用在存储函数,也可以用在存储过程。

游标的使用分为四步:

  • 创建游标;
  • 打开游标;
  • 通过游标获取记录;
  • 关闭游标

4.1、创建游标

在创建游标时,我们需要指定与游标关联的查询语句。

declare 游标名称 cursor for 查询语句;

如果在存储程序中也有声明局部变量的语句,则创建游标的语句一定要放在局部变量声明后面。

4.2、打开和关闭游标

创建好游标后,我们需要手动打开和关闭游标

open 游标名称;
close 游标名称;

打开游标意味着执行查询语句,使我们的游标和查询语句的结果集关联起来;关闭游标意味着释放与该游标相关的资源。一旦使用玩游标,就要把它关掉。如果不显式的关闭,在存储过程或者存储函数end语句执行完后,会自动关闭游标

4.3、通过游标获取记录

fetch 游标名 into 变量1,变量2,...,变量n;

 样例:

/**获取一条记录**/
CREATE PROCEDURE cursor_demo()
BEGIN
	DECLARE a_value INT;
	DECLARE b_value CHAR(1);
	DECLARE t1_record_cursor CURSOR FOR SELECT a1,b1,FROM t1;
	OPEN t1_record_cursor;
	FETCH t1_record_cursor INTO a_value,b_value;
	SELECT a_value,b_value;
	CLOSE t1_record_cursor;
END

/**获取表里的所有记录**/
CREATE PROCEDURE cursor_demo()
BEGIN
	DECLARE a_value INT;
	DECLARE b_value CHAR(1);
	DECLARE record_count INT;
	DECLARE i INT DEFAULT 0;
	DECLARE t1_record_cursor CURSOR FOR SELECT a1,b1,FROM t1;
	OPEN t1_record_cursor;
	WHILE IF i<record_count DO
		FETCH t1_record_cursor INTO a_value,b_value;
		SELECT a_value,b_value;
		SET i = i+1;
	END WHILE
	CLOSE t1_record_cursor;
END

4.4、变量结束时的执行策略

前面介绍的用来遍历结果集中记录的方式需要我们首先获得查询语句结果集中记录的条数

select count(*) from t1 into record_count;

获取条数的目的,是因为我们需要一个结束循环的条件:当调用fetch语句的次数与结果集中的记录条数相等的时候默认会停止存储函数或者存储过程的执行,并抛出错误。

我们可以在存储函数或者存储过程中事先声明一种针对某种错误的处理方式,这样服务器在执行的时候如果发生了错误,则不用停止执行向客户端发送错误提示,而是采用我们的错误处理方式来解决。

declare continue handler for not found 处理语句;

这样如果fetch语句获取不到记录时,只要在存储函数或者存储过程中写了这个SQL,就可以执行这个SQL了。

处理语句可以是简单的一条语句,也可以是由begin...end包裹的多条语句

样例:

CREATE PROCEDURE cursor_demo()
BEGIN
	DECLARE a_value INT;
	DECLARE b_value CHAR(1);
	DECLARE done INT DEFAULT 0;
	DECLARE t1_record_cursor CURSOR FOR SELECT a1,b1,FROM t1;
	DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
	OPEN t1_record_cursor;
	flag:LOOP
		FETCH t1_record_cursor INTO a_value,b_value;
		IF done=1 THEN
			LEAVE flag;
		END IF;
		SELECT a_value,b_value,done;
	END LOOP flag;
	CLOSE t1_record_cursor;
END

 这里面,fetch每次执行的时候都会判断以下done是否等于1,如果为1,证明结果集已经记录完毕,退出循环,不为1则一直循环读取记录。done的默认值是0,直到获取不到新的记录时,会执行set done =1.改为1后,循环结束。

5、触发器

存储程序包括存储例程(存储函数与存储过程)、触发器和事件,其中存储过程是需要我们手动调用的,触发器和事件是MySQL服务器在特定的情况下自己调用的。

介绍一些例子,了解一下触发器:

  • 在向t1表里面插入、更新数据之前自动对数据进行校验,要求a1列的值必须在1-10直接;规则:如果插入的记录的a1列的值小于1,则按照1插入;如果大于10则按照10插入
  • 在向t1表里面插入记录之后自动把这条记录插入到t2表

也就是对表的增删改查的操作前后,都可能需要让MySQL服务器自动执行一些额外的语句,这就是触发器的应用场景。

5.1、创建触发器

CREATE TRIGGER 触发器名
{BEFORE | AFTER }
{INSERT | UPDATE | DELETE }
ON 表名
FOR EACH ROW
BEGIN
	触发器内容
END

由大括号({})包裹并且内部用(|)分隔的语句表示必须在给定的选项中选取一个值。

{BEFORE | AFTER }表示触发器内容执行的时机 

 {INSERT | UPDATE | DELETE }表示对那种语句设置触发器。MySQL目前只支持对这三个设置触发器。 FOR EACH ROW BEGIN...END表示对具体语句影响的每一条记录都执行我们自定义的触发器内容

  • 对于insert语句来说,FOR EACH ROW影响的记录就是我们准备插入的那些记录;
  • 对于delete语句和update语句来说,FOR EACH ROW影响的记录就是符合where条件的那些记录(如果语句中没有where条件,则代表全部的记录)

如果触发器内容只包含一条语句,可以省略begin...end

因为MySQL服务器会对某条语句影响的所有记录依次调用我们自定义的触发器内容,所以针对每一条受影响的记录,我们需要一种方式来访问该记录中的内容。

MySQL提供了new和old分别来代表新纪录和旧记录

  • 对于insert语句设置的触发器来说,new代表准备插入的记录,old无效; 
  • 对于delete语句设置的触发器来说,old代表准备插入的记录,new无效; 
  • 对于update语句设置的触发器来说,new代表修改后的记录,old代表修改前的记录; 

话不多说,上例子,直接明了:

delimiter $
CREATE TRIGGER test_t1
BEFORE
INSERT
ON t1
FOR EACH ROW
BEGIN
	IF NEW.a1 < 1 THEN
		SET NEW.a1 = 1;
	ELSEIF NEW.a1 > 10 THEN
		SET NEW.a1 = 10;
	END IF;
END $
delimiter ;

其他的,大家自己去玩一下,我不一一展示了。

5.2、查看和删除触发器

用于查看当前数据库中定义的所有触发器语句如下所示:

show triggers;

用于查看某个具体的触发器的定义的语句

show create trigger 触发器名;

用于删除触发器的语句

drop trigger 触发器名;

5.3、触发器的注意事项

  • 触发器内容中不能有输出结果集的语句;
  • 触发器内容中NEW代表记录的值可以被改变,OLD代表记录的列的值无法更改(new代表的是insert或者update后的结果,而old代表的是之前的值,无法修改);
  • 在before触发器中,可以使用set new.列名=某个值的形式来更改待插入的记录或者待更新记录的某个列的值,但是这种操作不能在after触发器中使用,因为在执行after触发器的内容时的记录已经被插入完成或者更新完成了。

6、事件

让MySQL服务器在某个时间点或者每隔一段事件时间自动地执行一些语句,这就是事件。

6.1、创建事件

CREATE EVENT 事件名
ON SCHEDULE
{
	AT 某个确定的时间点 |
	EVERY 期望的时间间隔 [STARTS 开始日期和时间] [END 结束日期和时间]
}
DO
BEGIN
	具体的语句
END

事件支持两种类型的自动执行方式:在某个确定的时间点执行;每隔一段时间执行一次

  • 在某个确定的时间点执行
/**demo1**/
CREATE EVENT insert_test
ON SCHEDULE

	AT '2023-06-18 03:12:59'

DO
BEGIN
	INSERT INTO t1(a1,b1) VALUES(6,'f');
END

/***demo2*/
CREATE EVENT insert_test
ON SCHEDULE

	AT DATE_ADD(NOW(),INTERVAL 2 DAY)

DO
BEGIN
	INSERT INTO t1(a1,b1) VALUES(6,'f');
END
  • 每隔一段时间执行一次
/**demo3**/
CREATE EVENT insert_test
ON SCHEDULE
EVERY 1 HOUR
DO
BEGIN
	INSERT INTO t1(a1,b1) VALUES(6,'f');
END
/**demo4**/
CREATE EVENT insert_test
ON SCHEDULE
EVERY 1 HOUR STARTS '2023-06-18 04:12:59' ENDS '2023-06-18 13:12:59'
DO
BEGIN
	INSERT INTO t1(a1,b1) VALUES(6,'f');
END

时间单位大家可以去之前的文章查看一些其他的单位,比如minute、week等等。

6.2、查看和删除事件

用于查看当前数据库中定义的所有时间的语句:

show events;

用于查看某个具体的事件的定义的语句

show create event 事件名

用于删除某个事件

drop event 事件名;

MySQL基础(八)-----视图和存储程序。就分享到这个地方,后续会更新MySQL基础的最后一期,MySQL基础(九)-----备份与恢复、用户与权限、应用程序连接MySQL服务器。

今天的分享就到此结束了,如果觉得对您有帮助,麻烦给个三连!

以上内容为本人的经验总结和平时操作的笔记。若有错误和重复请联系作者删除!!感谢支持!!