0
点赞
收藏
分享

微信扫一扫

MySQL进阶(四)

高子歌 2022-05-05 阅读 46

四. 视图/存储过程/触发器

4.1 视图

是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且实在使用视图时动态生成的。

4.1.1 基本语法

  1. 创建
 create [or replace] view 视图名称[(视图列表)] as select语句 [with [cascaded | local] check option]
  1. 查询
查看创建视图语句:show create view 视图名称;
查看视图数据:select * from 视图名称....;
  1. 修改
方式一:create [or replace] view 视图名称[(列明列表)] as select语句 [with [cascaded | local] check option]
方式二:alter view 视图名称[(列表列名)] as select语句 [with [cascaded | local] check option]
  1. 删除
drop view [if exists] 视图名称 [视图名称]...
  1. 示例
#创建视图
create or replace view stu_v_1 as select id,name from student where id<=10;

#查询视图
show create view stu_v_1;
select * from stu_v_1;
select * from stu_v_1 where id<3;

#修改视图
create or replace view stu_v_1 as select id,name,no from student where id<=10;
alter view stu_v_1 as select id,name from student where id<=10;

#删除视图
drop view if exists stu_v_1;

4.1.2 检查选项

视图的检查选项
当使用with check option自居创建视图时,MySQL会通过视图检查正在更改的每个行,例如插入、删除、更新、以使其符合视图的定义。MySQL语序基于另一个视图创建视图,他还会依赖视图中的规则以保持一致性。为了确定检查的范围,mysql提供了两个选项:cascaded和local,默认值为cascaded。

cascaded:如果视图创建时定义了cascaded检查选项,那么在对视图进行更新操作时,不仅要去检查是否满足视图创建时的条件,还要递归地去检查是否满足他所依赖的视图的条件。

cascaded

创建视图v1时后面没有加with check option,此时对v1进行增删改操作他是不会去检查这个条件的。
create view v1 as select id,name from student where id<=20;
创建基于v1的视图v2,并且给他指定with cascaded check option,此时当我们操作v2这个视图时,由于cascaded
表示级联,他不仅要去检查当前操作是否满足v2的条件,还要去检查是否满足v2所依赖的v1的条件,就相当于在v1视图里
也加上了with cascaded check option,如果v1也关联了另外的视图同样如此。
create view v2 as select id,name from student where id>=10 with cascaded check option;


create or replace view stu_v_1 as select id,name from student where id<=20;
#插入成功,满足stu_v_1中的条件
insert into stu_v_1 values(5,'Tom');
#数据成功插入到基表中,然而视图中对应的数据并不存在,因为他不满足stu_v_1的条件。
insert into stu_v_1 values(25,'Tom');

create or replace view stu_v_2 as select id,name from 
stu_v_1 where id>=10 with cascaded check option;
#由于上面的视图创建语句加了with cascaded check option,首先要去检查是否满足stu_v_2中的条件,不满足,执行失败。
insert into stu_v_2 values(7,'Tom');
#首先检查是否满足stu_v_2中的条件,如果满足,再去检查stu_v_2所依赖的底层的所有视图,由于插入的数据不满足stu_v_1的条件,所以执行失败。
insert into stu_v_2 values(26,'Tom');
#插入成功,满足stu_v_2和stu_v_1中的条件。
insert into stu_v_2 values(26,'Tom');

create or replace view stu_v_3 as select id,name from 
stu_v_2 where id<=15;
#执行成功,满足stu_v_3,stu_v_2和stu_v_1中的条件。
insert into stu_v_3 values(11,'Tom');
#执行成功,因为stu_v_3中没有加with cascaded check option,不会去检查stu_v_3中的条件,而stu_v_3依赖
#stu_v_2,所以他去检查stu_v_2的条件,并且由于stu_v_2中加了with cascaded check option,于是他又向下去
#检查是否满足stu_v_1中的条件,发现条件都满足。
insert into stu_v_3 values(17,'Tom');
#执行失败,stu_v_3中没有with cascaded check option,所以不去检查stu_v_3的条件,而stu_v_3依赖stu_v_2
#视图,stu_v_2视图中有with cascaded check option,所以他去检查是否满足stu_v_2的条件,满足条件,然后再
#去检查stu_v_2所依赖的stu_v_1的条件,发现不满足,插入失败。
insert into stu_v_3 values(28,'Tom');

local:如果视图创建时定义了local检查选项,那么在对视图进行更新操作时,需要去检查是否满足创建时的条件。如果条件满足,再去看他所依赖的视图在创建时是否也定义了检查选项,如果定义了检查选项,他还要去检查他所依赖的视图中的条件,如果没有定义,则不做检查。

local

#往v1视图插入数据时,不会去检查v1中的条件
create view v1 as select id,name from student where id<=15;

#往v2视图插入数据时,由于v2在创建时定义了with local check option,所以他会去检查v2中的条件,然后再看v2
#所依赖的v1,v1视图在创建时没有定义检查选项,因此不检查v1中的条件。
create view v2 as select id,name from v1 where id>=10 with local check option;

create view v3 as select id,name from v2 where id<20;
create or replace view stu_v_4 as select id,name from student where id<=17;
#满足条件,直接插入到基表中
insert into stu_v_4 values(5,'Tom');
#不满足条件,但由于stu_v_4 在创建时没有定义检查选项,因此不对条件做检查,直接插入到基表中。
insert into stu_v_4 values(16,'Tom');

create or replace view stu_v_5 as select id,name from 
stu_v_4 where id>=10 with local check option;
#执行成功。由于stu_v_5在创建时定义了检查选项with local check option,因此他需要去检查是否满足stu_v_5的
#条件,如果满足,再去看他所依赖的视图stu_v_4,由于stu_v_4没有定义检查选项,所以不检查stu_v_4中的条件。
insert into stu_v_5 values(13,'Tom');
#执行成功。只需要检查stu_v_5中的条件,不需要检查stu_v_4中的条件
insert into stu_v_5 values(17,'Tom');


create or replace view stu_v_6 as select id,name from 
stu_v_5 where id<20;
#执行成功。首先由于stu_v_6中没有定义检查选项昂,所以不做检查。而他所依赖的stu_v_5中定义了检查选项,所以要
#检查,发现条件满足。然后在去看stu_v_5所依赖的stu_v_4,stu_v_4在创建时没有定义检查选项,因此也不做检查。
insert into stu_v_6 values(14,'Tom');

4.1.3 视图的更新

要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。如果视图包含以下任何一项,则视图不可更新:

  1. 聚合函数或窗口函数( sum()、min()、max()、count()等)
  2. distinct
  3. group by
  4. having
  5. union 或者 union all

示例:

使用聚合函数创建视图,并往视图中插入数据,可以看到数据插入失败。因为我们创建视图时使用了聚合函数,可以看到视图中的数据和表中的数据不是一一对应的,所以此时视图不能进行插入和更新。
在这里插入图片描述
视图的作用:

  • 简单:视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些经常被使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件。
  • 安全:数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据
  • 数据独立:视图可以帮助用户屏蔽真实表结构变化带来的影响。

4.2 存储过程

4.2.1 介绍

是实现经过变异并存储在数据库中的一段SQL语句的集合,盗用存储过程可以简化应用开发人员的很多工作,减少数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。主要思想:数据库SQL语言层面的代码封装与重用。

特点:
(1)封装,复用
(2)可以接受参数,也可以返回数据
(3)减少网络交互,效率提升

4.2.2 基本语法

  1. 创建
create procedure 存储过程名称([参数列表])
begin
		--SQL语句
endl
  1. 调用
call 名称([参数]);
  1. 查看
select * from information_schema.ROUTINES where ROUTINE_CHEMA='xxx'; --查询指定数据库的存储过程及状态信息
show create procedure 存储过程名称;  --查询某个存储过程的定义
  1. 删除
drop procedure [if exists] 存储过程名称;
  1. 示例
#创建
create procedure p1()
begin
		select count(*) from student;
end;

#调用
call p1();

#查看
select * from information_schema.ROUTINES where ROUTINE_SCHEMA='itcast';
show create procedure p1;

#删除
drop procedure p1;

注:在命令行中,执行创建存储过程的SQL时,需要通过关键字delimiter指定SQL语句的结束符。例如:delimiter $$

4.2.3 变量

  1. 系统变量是MySQL服务器提供,不是用户定义的,属于服务器层面,分为全局变量(global)、会话变量(session)。
    如果是全局变量,是针对所有会话有效的。如果是会话变量,仅在当前会话有效。

(1)查看系统变量

show [session | global] variables;    --查看所有系统变量
show [session | global] variables like '...';   --可以通过like模糊匹配方式查找变量
select @@[session | global] 系统变量名;   --查看指定变量的值

(2)设置系统变量

set [session | global] 系统变量名=值;
set @@[session | global] 系统变量名=值;

注:
如果没有指定session/global,默认是session,会话变量。
mysql服务器重新启动之后,所设置的全局参数会失效,要想不是小,可以在/etc/my.cnf中配置。

  1. 用户定义变量是用户根据需要自己定义的变量,用户变量不需要提前声明在用的时候直接用“@变量名"使用就可以。其作用域为当前连接。(两个@@指的是系统变量,一个@指的是用户字定义变量)

(1)赋值

set @var_name = expr [,@var_name = expr]...;
set @var_name := expr [,@var_name := expr]...;
select @var_name := expr [,@var_name := expr]...;
select 字段名 into @var_name from 表名;

(2)使用

select @var_name;

(3)示例

#赋值
set @myname = 'itcast';
set @myage := 10;
set @mygender := '男',@myhobby := 'java';

select @mycolor:= 'red';
select count(*) into @mycount from tb_user;

#使用
select @myname,@myage,@mygender,@myhobby;
select @mycolor,@mycount;

注:用户自定义的变量无需对其进行声明或初始化,只不过获取到的值为NULL。

  1. 局部变量是根据需要定义的在局部生效的变量,访问之前,需要declare声明。可用作存储过程内的局部比那辆和输入参数,局部变量的范围是在其内部声明的begin…end块。

(1)声明

declare 变量名 变量类型 [default...];
变量类型就是数据库字段类型:int, bigint, char, varchar, date, time等。

(2)赋值

set 变量名=值;
set 变量名 := 值;
select 字段名 into 变量名 from 表名...;

(3)示例


create procedure p2()
begin
	declare stu_count int default 0;
	select count(*) into stu_count from student;
	select stu_count;
end;
#局部变量的范围是在begin和end之间,超出这个范围就没用了。
call p2();

4.2.4 if判断

基本语法:

if 条件1 then
		...
else 条件2 then      --可选
		...
else			    --可选
		...
end if;

示例:

--if
--根据定义的分数score变量,判定当前分数对应的分数等级
--score>=85分,等级为优秀
--score>=60且score<85分,等级为及格
--score<60分,等级为不及格。

create procedure p3()
begin
	declare score int default 75;
	declare result varchar(10);
	if score >= 85 then
		set result := '优秀';
	else score >= 60 then
		set result := '及格';
	else
		 set result := '不及格';
	else if;
	select result;	  #查询结果为及格
end;		

4.2.5 参数

  1. 参数
类型含义备注
in该类参数作为输入,也就是需要调用传入值默认
out该类参数作为输出,也就是该参数可以作为返回值
inout既可以作为输入参数,也可以作为输出参数
  1. 用法
create procedure 存储过程名称([in/out/inout 参数名 参数类型])
begin
		--SQL语句
end;
  1. 示例
--根据传入(in)的参数score,判定当前分数对应的分数等级,并返回(out)
--score>=85分,等级为优秀
--score>=60且score<85分,等级为及格
--score<60分,等级为不及格。

create procedure p4(in score int,out result varchar(10))
begin
	declare result varchar(10);
	if score >= 85 then
		 set result := '优秀';
	else score >= 60 then
		 set result := '及格';
	else
		 set result := '不及格';
	else if;
end;	

call p4(68,@result);
select @result;	   #查询结果为及格

--将传入的200分制的分数,进行换算,换算成百分制,然后返回
create procedure p5(inout score double)
begin
	set score := score * 0.5;
end;

set @score = 78;
call p5(@score);
select @score;         #最后查询结果为39

4.2.6 case

  1. 语法一
case case_value
		when when_value1 then statement_list1
		[when when_value2 then statement_list2]...
		[else statement_list]
end case;
  1. 语法二
case
		when search_condition1 then statement_list1
		[when search_condition1 then statement_list1]...
		[else statement_list]
end case;
  1. 示例
--case
--根据传入的月份,判定月份所属的季节(要求采用case结构)。
--1-3月份,为第一季度
--4-6月份,为第二季度
--7-9月份,为第三季度
--10-12月份,为第三季度

create procedure p6(in month int)
begin
	declare result varchar(10);
	case 
		when month >=1 and month <= 3 then	
			set result := '第一季度';
		when month >=4 and month <= 6 then
			set result := '第二季度';
		when month >=7 and month <= 9 then
			set result := '第三季度';
		when month >=10 and month <= 12 then
			set result := '第四季度';
		else
			set result := '非法参数';
		
	select concat('您输入的月份为:',month,',所属的季度为:',result);		
end;

call p6(4);

4.2.7 循环

  1. while
    while循环是有条件的循环控制语句,满足条件后,再执行循环提中的SQL语句。
    具体语法为:
#先判定条件,如果条件为true,则执行逻辑,否则,不执行逻辑
while 条件 do
	SQL逻辑...
end while;

示例:

--while  计算从1累加到n的值,n为传入的参数值
--A. 定义局部变量,记录累加后的值
--B. 每循环一次,就会对n进行减1,如果n减到0,则退出循环

create procedure p7(in n int)
begin
	declare total int deault 0;
	while n>0 do
		set total := total + n;
		set n := n - 1;
	end while;
	select total;      #查询结果为55
end;

call p7(10);			
  1. repeat
    repeat是有条件的循环控制语句,退出循环。具体语法:
#先执行一次循环,然后判定逻辑是否满足,如果满足,则退出。如果不满足,则继续下一次循环
repeat
	SQL逻辑...
until 条件
end repeat;

示例:

--repeat  计算计算从1累加到n的值,n为传入的参数值
--A. 定义局部变量,记录累加后的值
--B. 每循环一次,就会对n进行减1,如果n减到0,则退出循环

create procedure p8(in n int)
begin
	declare total int deault 0;
	repeat 
		set total := total + n;
		n := n - 1;
	until n <= 0
	end repeat;
	
	select total;
end;

call p8(10);
call p8(5050);
  1. loop
    loop实现简单的循环,如果不在SQL逻辑中增加循环的条件,可以用其来实现简单的死循环。loop可以配合一下两个语句使用:
    (1)leave:配合循环使用,退出循环。
    (2)iterate:必须用在循环中,作用是跳过当前循环剩下的语句,直接进入下一次循环。
[begin_label:] loop
	SQL逻辑...
end loop [end_label];

leave lable;		  --退出指定标记的循环体
iterate lable;   --直接进入下一次循环

示例:

--loop  计算计算从1累加到n的值,n为传入的参数值
--A. 定义局部变量,记录累加后的值
--B. 每循环一次,就会对n进行减1,如果n减到0,则退出循环

create procedure p9(in n int)
begin
	declare total int deault 0;
	sum:loop 
		if n <= 0 then
			leave sum;
		end if;
		
		set total := total + n;
		n := n - 1;		
	end loop sum;
	
	select total;
end;

call p9(100);

--loop  计算计算从1累加到n的值,n为传入的参数值
--A. 定义局部变量,记录累加后的值
--B. 每循环一次,就会对n进行减1,如果n减到0,则退出循环: leave xx;
--C. 如果当前累加的数据是奇数,则直接进入下一次循环: iterate xx;

create procedure p10(in n int)
begin
	declare total int deault 0;
	sum:loop 
		if n <= 0 then
			leave sum;     #n小于等于0,退出循环
		end if;
		
		if n % 2 = 1 then
			set n := n - 1;
			iterate sum;    #n为奇数,进入下一次循环
		end if;
		
		set total := total + n;
		n := n - 1;		
	end loop sum;
	
	select total;     #1-100直接之间的偶数累加结果为2550
end;

call p10(100);   

4.2.8 游标

==游标(cursor)==是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标来对结果集进行循环的处理。游标的使用包含游标的声明、open、fetch、和close,其语法分别如下:
(1)声明游标

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

(2)打开游标

open 游标名称

(3)获取游标记录

fetch 游标名称 into 变量[,变量];

(4)关闭游标

close 游标名称;

(5)示例

根据传入的参数uage,来查询用户表tb_user中,所有的用户年龄小于等于uage的用户姓名(name)和专业(profession),
–并将用户的姓名和专业插入到所创建的一张新表(id,name,profession)中

--A. 声明游标,存储查询结果表
--B. 准备:创建表结构
--C. 开启游标
--D. 获取游标中的记录
--E. 插入数据到新表中
--F. 关闭游标

create procedure p11(in uage int)
begin
	#游标声明和普通变量声明是有先后顺序的,要先声明普通变量,再声明游标
	declare uname varchar(100);
	declare upeo varchar(100); 
	
	#创建游标
	declare u_cursor cursor for select name,professio from tb_user where age<=uage;   

	drop table if exists tb_user_pro;
	create table if not exists tb_user_pro(
		id int primary key auto_increment,
		name varchar(100),
		profession varchar(100)
	);
	
	open u_cursor;
	#循环获取游标中的数据
	while true do
		fetch u_cursor into uname,upro;     
		insert into tb_user_pro values(null,uname,upro);  #将获取到的数据插入到新表中
	end while;
	close u_cursor;  #关闭游标

end;

call p11(40);   
#执行失败,因为循环体中条件为while true,也就意味着我们没有退出循环,会
#一直循环下去。并且在循环过程中,每循环一次就要从游标中获取他的下一条记
#录,当循环到最后一条之后,再进行下一次循环时,游标中已经没有数据,此时再
#去获取游标中的数据将会报错退出循环。为解决当前问题,需要条件处理程序。

4.2.9 条件处理程序

==条件处理程序(handler)==可以用来定义流程控制结构执行过程中遇到问题时相应的处理步骤。具体语法:

declare handler_action handler for condition_value [,condition_value]... statement;

handler_action
	continue:继续执行当前程序
	exit:终止执行当前程序

condition_value
	sqlstate sqlstate_value:状态码,如02000
	sql warning:所有以01开头的sqlstate代码的简写
	not found:所有以02开头的sqlstate代码的简写
	sql exception:所有没有被sql warning或not found捕获的sqlstate代码的简写
	
--A. 声明游标,存储查询结果表
--B. 准备:创建表结构
--C. 开启游标
--D. 获取游标中的记录
--E. 插入数据到新表中
--F. 关闭游标

create procedure p11(in uage int)
begin
	#游标声明和普通变量声明是有先后顺序的,要先声明普通变量,再声明游标
	declare uname varchar(100);
	declare upeo varchar(100); 
	
	#创建游标
	declare u_cursor cursor for select name,professio from tb_user where age<=uage;   
	#02000状态码表示抓取时没有数据。当下面循环体抓取不到数据报错时就会进入到这个条件处理程序,在条件处理程序当中他会关闭游标然后执行退出操作。
	declare exit handler for SQLSTATE '02000' close u_cursor;
	# 也可以使用: delclare exit handler for not found close u_cursor;

	drop table if exists tb_user_pro;
	create table if not exists tb_user_pro(
		id int primary key auto_increment,
		name varchar(100),
		profession varchar(100)
	);
	
	open u_cursor;
	#循环获取游标中的数据
	while true do
		fetch u_cursor into uname,upro;     
		insert into tb_user_pro values(null,uname,upro);  #将获取到的数据插入到新表中
	end while;
	close u_cursor;  #关闭游标

end;

call p11(30); 

4.3 存储函数

存储函数是有返回值的存储过程,存储函数的参数只能是in类型的,具体语法如下:

create function 存储函数名称([参数列表)]
returns type [characteristic...]
begin
	--SQL语句
	return...;
end;

characteristic说明:
deterministic:相同的输入参数总是产生相同的结果。
no sql:不包含SQL语句。
reads sql data:包含读取数据的语句,但不包含写入数据的语句。

示例:

--存储函数
--从1累加到n
create function fun1(n int)
returns int deterministic
begin
	declare total int default 0;

	while n>0 do
		set total := total + n;
		set n := n - 1;
	end while;
	
	return total;
end;

select fun1(100);

弊端:必须得有返回值,因此能够使用存储过程替代就使用存储过程。

4.4 触发器

4.4.1 介绍

触发器是与表有关的数据库对象,指在insert/update/delete之前或之后,出发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志记录,数据校验等操作。
使用别名old和new来引用触发器中变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发器,不支持语句机触发器

触发器类型new 和 old
insert型触发器new表示将要或者已经新增的数据
update型触发器old表示修改之前的数据,new表示将要或已经修改后的数据
delete型触发器old表示将要或者已经删除的数据

4.4.2 语法

  1. 创建
create trigger trigger_name
before/after insert/update/delete
on table_name for each row     --行级触发器
begin
	trigger_stmt;
end;
  1. 查看
show triggers;
  1. 删除
drop trigger [schema_name.]trigger_name;  --如果没有指定chema_name,默认为当前数据库
  1. 示例
    (1)insert型触发器
    首先将SQL语句的结束符定义为$$,然后创建学生表student的触发器intrigger,当插入数据后,触发器将插入的学生姓名存储在stu_name当中。由图中所示,当插入数据后,能查询到插入的学生姓名。
    在这里插入图片描述
    (2)update型触发器
    已知学生表,创建student表的update型触发器,当更新表中学生名字后,存储学生原来的名字和更新后的姓名到old_name和new_name。当对表中某个学生姓名进行更改后,可以查询到他的新名和旧名。
    在这里插入图片描述
    (3)delete型触发器
    创建学生表的delete型触发器,当删除某个学生时,存储要删除的学生的姓名。如图中所示,当删除名为’WWC’的学生后,我们可以查询到被删除的学生的姓名。
    在这里插入图片描述
举报

相关推荐

JavaScript进阶(四)

Day四 CSS进阶

mysql(进阶)

MySQL进阶

Mysql进阶

MySQL进阶部分

MySQL进阶查询

mysql进阶学习

0 条评论