春华秋实之MySQL进阶-04 视图/存储过程/触发器

Posted :Concerto

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了春华秋实之MySQL进阶-04 视图/存储过程/触发器相关的知识,希望对你有一定的参考价值。

4 视图

4.1介绍以及基本语法

  1. 介绍
  • 概念:视图是一种虚拟的存在的表。视图的行和列数据来自定义视图的查询中使用的表,并且是使用视图时动态生成的。
  1. 创建
create view 视图名称 as select 语句;
create or replace view course_v_1 as
    select name from course where name = mysql;

  1. 查询视图
查看创建视图语句:show create view 视图名称;
查看视图数据:select * from 视图名称;
show create view course_v_1;
select * from course_v_1;//视图查询也可以加上where条件

  1. 修改视图
方式一:create or replace view 视图名称 as select 语句;
方式二:alter view 视图名称 as select 语句;
create or replace view course_v_1 as
    select id,name from course where name = MySQL;
alter view course_v_1 as
    select id,name from course where name = MySQL;
  1. 删除视图名称
drop view [if exist]视图名称;
drop view if exists course_v_1;

4.2 检查选项

  1. 视图的增与改
insert into 视图名字 values (字段值,字段值);
  1. 检查项
  • 创建视图时候增加检查项
create or replace view 视图名称 as select 语句 with cascaded/local check option;
create or replace view course_v_1 as
    select id,name from course where id <=20
    with cascaded check option;
  • 当插入不符合视图条件的值的时候,便会提示阻止插入

  1. cascaded检查项
-- 创建视图
create or replace view course_v_1 as
    select id,name from course where id <=20;

create or replace view course_v_2 as
    select id,name from course_v_1 where id >=10
    with cascaded check option;

-- 插入数据
insert into course_v_2 values (6,Airflow);
insert into course_v_2 values (25,GO);
insert into course_v_2 values (15,VB);
  • 插入(6,Airflow)不行是因为视图2 有检查项进行了限制

  • 插入(25,GO)不行是因为,cascaded是级联,也会根据视图1中的条件进行限制

  1. 不含cascaded级联检查项
-- 创建视图
create or replace view course_v_1 as
    select id,name from course where id <=20;

create or replace view course_v_2 as
    select id,name from course_v_1 where id >=10
    with cascaded check option;

create or replace view course_v_3 as
    select id,name from course_v_2 where id <=15;

insert into course_v_3 values (11,Python);
insert into course_v_3 values (17,Python);
insert into course_v_3 values (28,Python);    
  • 插入(11,Python)成功是因为,每个视图条件都满足
  • 插入 (17,Python)成功是因为,视图3没有检查项,不满足视图3的条件也可以,但是视图2有检查项,从而会去查找视图2中的检查项是否满足

  • 插入(28,Python)不可以是因为,不满足视图2中检查项级联到视图1 的条件:id<=20
  1. local检查项
  • 插入(13,SpringBoot):会检查本视图中的条件,向上检查依赖的视图时,取决于依赖的视图是否还有检查项,如果有,就继续,如果没有,就不检查,此案例没有,只检查id>=10

  • 插入(14,SpringBoot)成功,是因为视图6的条件符合,查找依赖的视图5,发现有local检查项,那检查,继续往上视图4没local检查项,因此不检查

4.3 视图的更新与作用

  1. 不可更新的场景
  • 存在聚合函数和窗口函数
create or replace view course_v_count as
    select COUNT(*) from course;

-- 执行失败,报错:不可执行插入操作
insert into course_v_count values(11);
[HY000][1471] The target table course_v_count of the INSERT is not insertable-into
  • ​ DISTINCT
  • GROUP BY
  • HAVING
  • UNION和UNION ALL
  1. 可更新条件
  • 想要视图可更新,视图中的行和基础中的行之间必须存在一一对一的关系
  1. 视图的作用
  • 操作简单。针对经常被使用的查询可以定义为视图,从而使得用户不必为以后的操作每次指定全部的条件
  • 安全。通过视图用户只能查询和修改他们所见的数据(可以控制到字段)
  • 数据独立。可以帮助用户屏蔽真实表结构带来的影响。例如字段名称的改变,可以根据对视图进行更新操作即可,可以起别名的方法屏蔽基表的变化,减少对业务的影响。

4.4 视图的案例

  1. 为了保证数据库表的安全性,开发人员在操作tb_user表的时候,只能看到用户的基本字段,屏蔽手机号和邮箱两个字段
create or replace view tb_user_view as
    select
            id,name,profession,age,gender,status,createtime
    from
            tb_user;

-- 后续直接查询视图
select * from tb_user_view;
  1. 查询每个学生所选修的课程(三张表联查),这个功能在很多的业务中都有使用到,为了简化操作,定义一个视图。
create view tb_stu_course as 
select 
    s.name studentname,s.no student_no,c.name course_name 
from 
    student s ,student_course sc,course c 
when 
    s.id = sc.studentid 
and 
    sc.courseid = c.id;

-- 后续直接查询视图
select * from tb_stu_course;    

5 存储过程

5.1 介绍以及基本语法

  1. 介绍
  • 存储过程是事先经过编译在数据库中的一段SQL语句的集合,调用存储过程可以简化应用人员开发的很多工作。
  • 思想就是将数据库SQL语言层面的代码封装与重用
  1. 特点
  • 封装,复用
  • 可以接收参数,也可以返回数据
  • 减少网络交互,效率提升
  1. 基本语法
  • 创建存储过程
create procedure 存储过程名称([参数列表])
begin

-- SQL语句
end;
create procedure p1()
begin
    select count(*) from tb_user;
end;

  • 命令行下创建存储过程
-- 因为命令行中见到分号默认结束,因此先定义结束符
mysql> delimiter $$

mysql> create procedure p1()
mysql> begin
mysql>    select count(*) from tb_user;
mysql> end
mysql>$$
  • 调用
call 名称([参数]);
call p1();
  • 查看
select * from information_schema.ROUTINES
where ROUTINE_SCHEMA = itcast;
-- information_schema是库,routines是表,routine_schema是字段
select * from information_schema.routines where routine_schema = xxx;

show create procedure p1;
  • 删除
drop procedure [if exist] 存储过程名称; 

5.2 系统变量

  1. 变量的分类
    • 系统变量:MySQL服务器提供的,不是用户定义的,属于服务器层面。分为全局变量(GLOBAL),会话变量(SESSION)
    • 全局变量:在所有的会话都是有效的
    • 会话变量:仅仅在此会话中有效
  2. 查看系统变量
show [session/global] variables; --查看所有系统变量
show [session/global] variables like ...... -- 可以通过like模糊匹配方式查找变量
select @@[session/global] 系统变量名  -- 查看指定变量的值
  • 语法
show session variables;
  • 查询事务自动提交的变量
show session variables like auto%;

  • 如果记得名称,就用select+@@全名
select @@session.autocommit;
  1. 设置系统变量
  • 语法
set [session/global] 系统变量名 = 值;
set @@[session/global] 系统变量名 = 值;
  • 把自增改成0
set session autocommit = 0;
set @@session.autocommit = 0;
  • 自动提交关闭后,插入语句发现表还是没有变化,因为需要手动commit提交下

  • 但是改的是会话变量,别的会话框查还是1

  • 改成全局变量看看,发现重启后不生效,变回了1
  • 如果要改,去/etc/my.cnf配置文件中改
set @@global.autocommit = 0;
select @@global.autocommit;

5.3 用户定义变量

  1. 概念
  • 是用户根据需要自己定义的变量,用户变量不同提前声明,用的时候@变量名使用即可。其作用域为当前连接。
  1. 赋值
  • 语法
set @变量名 = 值;
set @变量名 := 值;

select @变量名 := 值;
select 字段名 into @变量名 from 表名;
  • 案例:赋值可以多个一起
set @myname = 芬芬;
set @myage := 18;
set @mygender := 女,@myhobby:=大数据;

select @mycolor := red;
select COUNT(*) into @mycount from tb_user;
  1. 查看
  • 语法
select @变量名;
  • 案例
select @myname,@myage,@mygender,@myhobby;

select @mycolor,@mycount;

-- 就算没赋值,也可以查,就是0而已
select @abc;

5.4 局部变量

  1. 含义
  • 是根据需要定义的局部生效的变量,访问之前,需要declare声明。可用作存储过程内的局部变量和输入参数,局部变量的范围是在其声明的begin...and之间
  1. 声明
declare 变量名 变量类型[default...];
变量类型有:int,bigint,char,varchar,date,time等
  1. 赋值
set 变量名 = 值;
set @变量名 := 值;

select @变量名 := 值;
  1. 案例
-- 声明
-- 赋值
create procedure p2()
begin
    declare course_count int default 0;
    set course_count :=100;
    select course_count;
end;

-- 调用
call p2();

5.5 if判断

  1. 语法
if 条件1 then
...
elseif 条件2 then
...
else
...
end if;
  1. 案例

  • score是58 写shi了
create procedure p3 ()
begin
    declare score int default 58;
    declare result varchar(10);

    if score >= 85 then
        set result := 优秀;
    elseif score >= 60 then
        set result := 及格;
    else
        set result := 不及格;
    end if;
    select result;
end;

call p3();
  • 传个参数的写法
见下章5.5 参数的传参写法

5.5 参数(in,out,inout)

  1. 参数分类
  • in:作为输入
  • out:作为输出
  • inout:作为输入输出
  1. 语法
create procedure 存储构成名称(in/out/inout 参数名 参数类型)
begin

    --sql语句

end;
  1. 案例
  • 需求1:

create procedure p4 (in score int,out result varchar(10))
begin
    -- declare score int default 58;
    -- declare result varchar(10);

    if score >= 85 then
        set result := 优秀;
    elseif score >= 60 then
        set result := 及格;
    else
        set result := 不及格;
    end if;
    -- select result;
end;

-- 第二个参数需要一个变量来接收,然后打印出来
call p4(68,@result);
select @result;
  • 需求2:
将传入的200分制的分数,进行换算,换算成百分制,然后返回分数
分析:分数即使传入值,也是传出值,用inout
create procedure p5 (inout score int)
begin
    set score := score * 0.5;
end;

set @score := 178;   -- 初始化
call p5(@score);    -- 调用
select @score;      -- 打印

5.6 case判断

  1. 语法
case 字段
    when 条件 then 程序
    when 条件 then 程序
    else 程序
end case;
  1. 案例

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:= 非法参数;
    end case;

    select concat(你输入的月份为,month,所属的季度为,result);
end;

call p6(4);

5.7 while循环

  1. 语法
while 条件 do
    SQL逻辑
end while;
  1. 案例
  • 计算从1累加到n的值,n为传入的参数值
create procedure  p7 (in n int)
begin
    declare total int default 0;
    while n > 0 do
        set total:= total + n;
        set n := n - 1;
    end while;
    select total ;
end;

call p7(10);

5.8 repeat 循环

  1. 概念:repeat 是有条件的循环控制语句,当满足条件的时候退出循环
  2. 语法:有dowhile那味儿
repeat 
    SQL逻辑
    until 条件
end repeat;
  1. 案例
  • 计算从1累加到n的值,n为传入的参数值
create procedure p8(in n int)
begin
    declare total int default 0;

    repeat
        set total:= total+n;
        set n:=n-1;
    until n<=0
    end repeat;

    select total;
end;

call p8(10);
call p8(100);

5.9 loop循环

  1. 概念

如果不在SQL逻辑中增加退出循环的条件,可以用来实现简单的死循环,LOOP一般配合

  • leave:退出
  • iterate:跳过当前循环剩下的语句,直接进入下一次循环
  • 类比break和continue
  1. 语法
begin_label :LOOP
    SQL逻辑
END LOOP end_label;
  1. 案例
  • 计算从1累加到n的值,n为传入的参数值
create procedure p9(in n int)
begin
    declare total int default 0;

    sum:loop
        if n<=0 then
            leave sum;
        end if;

        set total:=total + n;
        set n := n-1;

    end loop sum;

    select total;

end;

call p9(10);
call p9(100);
  • 计算从1到n之间的偶数累加的值,n为传入的参数值
create procedure p10(in n int)
begin
    declare total int default 0;

    sum:loop
        if n<=0 then
            leave sum;
        end if;

        if  n%2 = 1 then
            set n := n-1;  -- 这部是因为奇数的时候回跳过下面的n-1,因此上面也减一次
            iterate sum;
        end if;

        set total:=total + n;
        set n := n-1;

    end loop sum;

    select total;

end;

call p10(10);
call p10(100);

5.10 cursor 游标

  1. 场景

  • select*赋值给局部变量,报错,原因是把整张表赋值给到int类型的变量,变量是只能接收单行单列的数据
  • 针对这种情况,游标出场啦
  1. 概念和内容
  • 概念:是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环的处理。

  • 内容:游标的声明、OPEN、CLOSE
  1. 游标的语法
  • 游标的声明
declare 游标名 cursor for 查询语言;
  • 打开游标
open 游标名;
  • 获取游标记录
fetch 游标名 into 变量;
  • 关闭游标
close 游标名;
  1. 案例

create procedure p11(in uage int)
begin

    -- 声明两个变量,方便后续赋值,普通变量声明要在声明游标之前
    declare uname varchar(100);
    declare upro varchar(100);
    -- 声明游标
    declare u_cursor cursor for select name,profession 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);-- null是自增的id,不用慌

    end while;

    -- 关闭游标
    close u_cursor;

end;
  • 但是会报错,因为循环那边循环第一次后,游标的数值给了新表,游标里面没数据,第二次循环的时候从游标中获取的记录为空

5.11 handler 条件处理过程

  1. 概念

    可以用来定义在流程控制结构执行构成中遇到问题时相应的处理步骤,类比异常处理

  2. 语法
declare handler_action HANDLER FOR condition_value [condition_value]...statment;

handler_action:

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

condition_value:
如下图一部分,如有兴趣去MySQL官方文档那边看下各个状态码的用途

  1. 案例
  • 承上例,多了个handler,声明一个条件处理程序,当满足SQL状态码02000的时候触发退出操作,退出的时候并且关闭游标
create procedure p11(in uage int)
begin

    -- 声明两个变量,方便后续赋值,普通变量声明要在声明游标之前
    declare uname varchar(100);
    declare upro varchar(100);
    -- 声明游标
    declare u_cursor cursor for select name,profession from tb_user where age <= uage;

    -- 声明handler
    declare exit handler for SQLSTATE 02000 close u_cursor;
    -- 也可以 declare exit handler for not found close u_cursor;表示处理02开头的状态码
    -- 创建一张表
    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);

5.12 存储函数

  1. 概念
  • 存储函数是有返回值的存储过程,存储函数的参数只能是in类型。
  1. 语法
create function 存储函数名称[参数列表]
returns type [参数]
begin
    --SQL语句
    return...;
end;

参数:
deterministic:相同的输入参数总是产生相同的结果
no sql:不包含SQL
read sql data:包含读取数据的语句,但不包含
  1. 案例
  • 计算从1累加到n的值,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);

6 触发器

6.1 介绍

  1. 触发器是与表有关的数据库对象,指在insert/update/delete之前或之后,触发并执行触发器中定义的SQL语句集合

  2. 可以协助应用在数据库段确保数据的完整性,日志记录,数据校验等操作。

  3. 使用别名OLD和NEW来引用触发器中发生变化的记录内容

6.2 语法

  1. 创建
create trigger trigger_name 
before/after/ insert/update/delete  --指定类型触发器 
on tbl_name for each row --行级触发器
begin
    trigger_stmt;
end
  1. 查看
 show triggers;
  1. 删除
drop trigger trigger_name;

6.3 案例

  1. 新增数据的触发器案例

​ 通过触发器记录tb_user的数据的变更日志,将变更日志插入到日志表user_logs表中,包含增,改,删

  • 创建表结构
create table user_logs(
  id int(11) not null auto_increment,
  operation varchar(20) not null comment 操作类型, insert/update/delete,
  operate_time datetime not null comment 操作时间,
  operate_id int(11) not null comment 操作的ID,
  operate_params varchar(500) comment 操作参数,
  primary key(`id`)
)engine=innodb default charset=utf8;
  • 创建触发器
-- 插入数据时的触发器
create trigger tb_user_insert_trigger
    after insert on tb_user for each row
    begin
        insert into user_logs(id, operation, operate_time, operate_id, operate_params) values
        (null,insert,now(),new.id,-- null 表示默认id自增
            concat(插入数据的内容为:id=,NEW.id,,name=,new.name,,phone=,new.phone,,email=,new.email,,profession=,new.profession));
    end;
  • 查看触发器
show triggers ;
  • 在tb_user表中插入数据,验证触发器
 insert into tb_user(id, name, phone, email, profession, age, gender, status, createtime)
 VALUES (25,二皇子,18809091212,erhuangzi@163.com,软件工程,23,1,1,now());
  • 发现user_logs日志表中有数据

  1. 修改数据的触发器
  • 创建修改的触发器operate_params有区分OLD和NEW之分
-- 和insert触发器不同的点是:
create trigger tb_user_update_trigger
    after update on tb_user for each row
    begin
        insert into user_logs(id, operation, operate_time, operate_id, operate_params) values
        (null,update,now(),new.id,-- null 表示默认id自增
        concat(更新之前的数据:id=,OLD.id,,name=,OLD.name,,phone=,OLD.phone,,email=,OLD.email,,profession=,OLD.profession,
        |更新之后的数据:id=,NEW.id,,name=,new.name,,phone=,new.phone,,email=,new.email,,profession=,new.profession));
    end;
  • 查看触发器
show triggers ;
  • 在tb_user表中更新数据,验证触发器
 update tb_user set age = 20 where id = 23;
 update tb_user set age = 20 where id <= 5;   -- 因为是行级触发器,所以回触发5次
  • 更新后,发现user_logs日志表多了5行数据

  1. 删除的触发器
  • 创建,注意只有OLD没有NEW了
-- 删除数据时的触发器
create trigger tb_user_delete_trigger
    after delete on tb_user for each row
    begin
        insert into user_logs(id, operation, operate_time, operate_id, operate_params) values
        (null,delete,now(),OLD.id,-- null 表示默认id自增
        concat(更新之前的数据:id=,OLD.id,,name=,OLD.name,,phone=,OLD.phone,,email=,OLD.email,,profession=,OLD.profession));
    end;
  • 查看下新建的触发器
show triggers ;
  • 在tb_user表中删除数据,验证触发器
delete from tb_user where id = 25;
  • 删除后,发现user_logs日志表多了数据

以上是关于春华秋实之MySQL进阶-04 视图/存储过程/触发器的主要内容,如果未能解决你的问题,请参考以下文章

春华秋实之MySQL进阶-02 索引

春华秋实之MySQL进阶-05 锁

MySQL进阶篇之视图/存储过程/触发器

MYSQL进阶学习笔记四:MySQL存储过程之定义条件,处理过程及存储过程的管理!(视频序号:进阶_11,12)

超详细图解!MySQL进阶篇存储过程,视图,索引,函数,触发器

MYSQL进阶学习知识拓展一:MySQL 存储过程之游标!