一、视图

视图:是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。

视图的特点:
  1.视图的列可以来自不同的表,是表的抽象和逻辑意义上建立的新关系;
  2.视图是由基本表(实表)产生的表(虚表);
  3.视图的建立和删除不影响基本表;
  4.对视图内容的更新(添加、删除和修改)直接影响基本表;
  5.当视图来自多个基本表时,不允许添加和删除数据。

-- 创建视图
  create view 视图名 as sql语句;
  create view v1 as select id,name from userinfo;
-- 使用视图
  select * from 视图名;
  select * from v1;
-- 更新视图
  alter view 视图名 as sql语句;
  alter view v1 as select id,name from userinfo where id<10;
-- 删除视图
    drop view 视图名;
    drop view v1;

二、触发器

触发器:监视某种情况,并触发某种操作;对某个表进行【增/删/改】操作的前后如果希望触发某个特定的行为时,可以使用触发器,触发器用于定制用户对表的行进行【增/删/改】前后的行为。

触发器创建语法四要素:
  1.监视地点(table)
  2.监视事件(insert/update/delete)
  3.触发时间(after/before)
  4.触发事件(insert/update/delete)

-- 创建触发器语法        
create trigger 触发器名 after/before insert/update/delete
  on 表名 for each row  -- 这句是固定的
begin
  -- begin和end中间放置需要执行的sql语句
end

after/before:只能选一个 , after(后置触发), before(前置触发)
insert/update/delete:只能选一个

 下面来介绍如何使用触发器

-- 商品表
create table goods_tb(
  id int primary key auto_increment,
  name varchar(20),
  num int
);

-- 订单表
create table order_tb(
    oid int primary key auto_increment,
    gid int,
    gnum int
);

-- 添加3条商品数据
insert into goods_tb(name,num) values('商品1',10),('商品2',10),('商品3',10);

 如果我们在没使用触发器之前,假设我们现在卖了3个商品1,则我们需要做两件事:

-- 1.往订单表插入一条记录
insert into order_tb(gid,gnum) values(1,3);

-- 2.更新商品表商品1的剩余数量
update goods_tb set num=num-3 where id=1;

现在,我们来创建一个触发器:

create trigger tg1 after insert
  on order_tb for each row
begin
  update goods_tb set num=num-3 where id=1;
end

这时候我们只要执行:

insert into order_tb(gid,gnum) values(1,3);

会发现商品1的数量变为7了,说明在我们插入一条订单的时候,触发器自动帮我们做了更新操作。

但现在会有一个问题,因为我们触发器里面num和id都是写死的,所以不管我们买哪个商品,最终更新的都是商品1的数量。比如:我们往订单表再插入一条记录:

insert into order_tb(gid,gnum) values(2,3);

执行完后会发现商品1的数量变4了,而商品2的数量没变,这显然不是我们想要的结果。我们需要改进之前创建的触发器。

我们需要知道在触发器中引用行的值,也就是说要得到新插入的订单记录中的gid或gnum的值。

对于insert而言即将插入的数据行用new来表示,行中的每一列的值用 new.字段名  来表示。

所以可以这样来修改触发器:

create trigger tg2 after insert
  on order_tb for each row
begin
  update goods_tb set num=num-new.gnum where id=new.gid;
end

第二个触发器创建完毕后,把第一个触发器删掉:

drop trigger tg1;

再来测试一下,插入一条订单记录:

insert into order_tb(gid,gnum) values(2,3);

执行完发现商品2的数量变为7了,现在就对了。

但是,现在还存在两个其他的问题:

# 1.当用户撤销一个订单的时候,我们这边直接删除一个订单,我们是不是需要把对应的商品数量再加回去呢?
对于delete而言即将删除的数据行用old来表示,原表本来有一行,但客户取消了交易(相当于删除了这一行数据),如果想引用被删除的这一行,用 old.字段名 可以引用删除的值。
那我们的触发器就该这样写:

 客户取消订单,则触发器该这样设计:

create trigger tg3 after delete
  on order_tb for each row
begin
  update goods_tb set num=num+old.gnum where id=old.gid;
end    

MySQL之视图、触发器、存储过程、函数、事务、数据库锁

假如客户取消了交易,那么就会执行下面这条sql语句:

delete from order_tb where id=1;

 这时,我们再看看商品归还没有;

MySQL之视图、触发器、存储过程、函数、事务、数据库锁

一看,客户的订单成功取消了,商品也归还了,非常好。

# 2.当客户修改一个订单的数量时,我们触发器修改怎么写?

 MySQL之视图、触发器、存储过程、函数、事务、数据库锁

还是上面这个数据:假如客户觉得数量少了,需要改为订购5个商品1,则触发器该这样设计:

create trigger tg4 after update
  on order_tb for each row
begin
  update goods_tb set num=num+old.gnum-new.gnum where id=old.gid;  -- new表示即将插入的数据行,old表示即将删除的数据行。(这里num=7+3-5)
end

执行以下sql语句:

update order_tb set gnum=5 where id=1;

 MySQL之视图、触发器、存储过程、函数、事务、数据库锁

成功完成操作啦

三、存储过程

存储过程:存储过程是为了完成某个数据库中的特定功能而编写的语句集合;该语句集包括SQL语句(对数据的增删改查)、条件语句和循环语句等......当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行。

-- 看现有的存储过程
    show procedure status;
    
-- 创建存储过程(无参数示例)
    create procedure p1()
    begin
    select * from t1;
    end
    
-- 调用存储过程
    call 存储过程名称(参数类型 参数名 数据类型);

-- 删除存储过程
    drop procedure 存储过程名称;

有参数存储过程:

create procedure p2(in i int,inout io varchar(50))
begin
update goods_tb set name=io where id=i;
end

set @io="商品100";   # 设置初始值
call p2(3,@io);     # 执行存储调用
select @io;         # 拿到结果

MySQL之视图、触发器、存储过程、函数、事务、数据库锁 
create procedure p3(in i int,out o varchar(50))
begin
select name into o from teacher where id = i;
end

set @name=null;
call p3(1,@name);
select @name;

MySQL之视图、触发器、存储过程、函数、事务、数据库锁

对于存储过程,可以接收参数,其参数有三类:
in           仅用于传入参数用
out         仅用于返回值用
inout      既可以传入又可以当作返回值

PS: into关键字可以将前面字段的查询结果执行给 into 后面的变量

create procedure p_in(in num int)
begin
    select num;
    set num=100;
    select num;
end;

set @num=1;
call p_in(@num);
select @num;

# 总结: in 参数只是将变量在存储过程内部做了修改,并没有影响到外部,@num仍为1。
in

相关文章: