1. 视图
视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。
通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。
1.1 创建视图
1 2 3 4 CREATE [ OR REPLACE ] VIEW 视图名称[(列名列表)] AS SELECT 语句 [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]create or replace view stu_v1 as select id,name from student where id<= 10 ;
1.2 查询视图
查看创建视图语句: SHOW CREATE VIEW
视图名称;
show create view stu_v_1;
查看视图数据:SELECT*FROM
视图名称;
1.3 修改视图
方式一:CREATE[OR REPLACE] VIEW 视图名称[(列名列表))] AS SELECT 语句[ WITH[ CASCADED | LOCAL ] CHECK OPTION ]
方式二:ALTER VIEW 视图名称 [(列名列表)] AS SELECT语句 [WITH [CASCADED | LOCAL] CHECK OPTION]
1.4 删除视图
DROP VIEW [IF EXISTS] 视图名称 [视图名称]
1.5 视图检查选项
当使用WITH CHECK QPTION子句创建视图时,
MySQL会通过视图检查正在更改的每个行,例如插入,更新,删除,以使其符合视图的定义。
MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。
为了确定检查的范围,mysql提供了两个选项:CASCADED 和 LOCAL
,默认值为 CASCADED。
NOTE:如果没有开检查选项就不会进行检查。不同版本是不同含义的,要看版本。
1.5.1 CASCADED
级联,一旦选择了这个选项,除了会检查创建视图时候的条件,还会检查所依赖视图的条件。
比如下面的例子:创建stu_V_l 视图,id是小于等于 20的。
create or replace view stu_V_l as select id,name from student where id <=20;
再创建 stu_v_2 视图,20 >= id >=10。
create or replace view stu_v_2 as select id,name from stu_v_1 where id >=10 with cascaded check option;
再创建 stu_v_3 视图。
create or replace view stu_v_3 as select id,name from stu_v_2 where id<=15;
这条数据能够成功,stu_v_3 没有开检查选项所以不会 去判断 id
是否小于等于15, 直接检查 是否满足 stu_v_2。
insert into stu_v_3 values(17,'Tom');
1.5.2 LOCAL
本地的条件也会检查,还会向上检查。在向上找的时候,就要看是否上面开了检查选项,如果没开就不检查。和
CASCADED 的区别就是 CASCADED 不管上面开没开检查选项都会进行检查。
1.6 更新及作用
要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。如果视图包含以下任何一项,则该视图不可更新
聚合函数或窗口函数 ( SUM()、MIN()、MAX()、COUNT() 等 )
DISTINCT
GROUP BY
HAVING
UNION 或者UNION ALL
例子: 使用了聚合函数,插入会失败。
create view stu_v_count as select count(*) from student;
insert into stu_v_count values(10);
作用
总而言之
类似于给表加上了一个外壳,通过这个外壳访问表的时候,只能按照所设计的方式进行访问与更新。
1.7 案例
1 2 3 4 5 6 7 8 9 10 create view tb_user_view as select id, name, profession, age, gender, status, createtime from tb_user;select * from tb_user_view;create view tb_stu_course_view as select s.name student_name, s.no student_no, c.name course_name from student s, student_course sc, course c where s.id = sc.studentid and sc.courseid = c.id;select * from tb_stu_course_view;
2. 存储过程
封装
复用
可以接收参数,也可以返回数据减少网络交互,效率提升
2.1 创建
1 2 3 4 5 6 7 8 9 10 11 12 CREATE PROCEDURE 存储过程名称( [参数列表] ) BEGIN SQL 语句 END ; delimiter $$create procedure p1()begin select count (* ) from users;end $$ delimiter ;
NOTE: 在命令行中,执行创建存储过程的SQL时,需要通过关键字delimiter
指定SQL语句的结束符。默认是 分号作为结束符。
delimiter ,则符作为结束符。
2.2 调用
CALL 名称 ( 参数 )
2.3 查看
查询指定数据库的存储过程及状态信息
SELECT* FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA = 'xxx'
存储过程名称;--查询某个存储过程的定义
SHOW CREATE PROCEDURE 存储过程名称
1 2 select * from information_schema.routines where routine_schema = 'itheima' ;show create procedure p1;
2.4 删除
DROP PROCEDURE [ IFEXISTS ] 存储过程名称
2.5 变量
2.5.1 系统变量
系统变量 是 MySQL
服务器提供,不是用户定义的,属于服务器层面。分为全局变量(
GLOBAL
)、会话变量( SESSION
)。
查看系统变量
1 2 3 show [SESSION| GLOBAL ] VARIABLES; SHOW [SESSION| GLOBAL ] VARIABLES LIKE '...' ; SELECT @@[SESSION| GLOBAL ] 系统变量名;
设置系统变量
1 2 SET [SESSION| GLOBAL ] 查看变量名= 值;SET @@[SESSION| GLOBAL ] 系统变量名= 值;
注意:
如果没有指定 SESSION/GLOBAL
,默认是
SESSION
,会话变量。
MySQL
服务重新启动后,所设置的全局变量参数会失效,要想不失效,可以在
`/etc/my.cnf/ 中配置。
2.5.2 用户定义变量
用户定义变量
是用户根据需要自己定义的变量,用户变量不同提前声明,在用的时候直接用"@变量名 "使用就可以。其作用域为当前连接。
赋值
1 2 SET @var_name = expr [,@var_name = expr]...;SET @var_name := expr [,@var_name := expr]...;
1 2 SELECT @var_name := expr [,@var_name := expr]...;SELECT 字段名 INTO @var_name FROM 表名;
使用
案例
1 2 3 4 5 6 7 8 9 10 11 12 set @myname = 'itcast' ;set @mysqg := 10 ;set @mygender := '男' ,@myhobby := 'java' ;select @mycolor := 'red' ;select count (* ) into @mycount from tb_user;select @myname ,@myage ,@mygender ,@myhobby ;select @mycolor ,@mycount ;
注意:
用户定义的变量无需对其进行声明或初始化,只不过获取到的值为
NULL
。
2.5.3 局部变量
局部变量
是根据需要定义的在局部生效的变量,访问之前,需要 DECLARE
声明。可用作存储过程内的局部变量和输入参数,局部变量的范围是在其内声明的
BEGIN ... END
块。
声明
1 DECLARE 变量名 变量类型 [DEFAULT ...];
变量类型就是数据库字段类型: INT
、 BIGINT
、 CHAR
、 VARCHAR
、 DATE
、
TIME
等
赋值
1 2 3 SET 变量名 = 值;SET 变量名 := 值;SELECT 字段名 INTO 变量名 FROM 表名 ...;
案例
1 2 3 4 5 6 7 8 create procedure p()begin declare stu_count int default 0 ; select count (* ) into stu_count from student; select stu_count;end ;call p();
2.6 if
语法:
1 2 3 4 5 6 7 IF 条件1 THEN ... ELSEIF 条件2 THEN ...ELSE ...END IF;
案例
根据定义的分数 score
变量,判定当前分数对应的分数等级。
score >= 85 分,等级为优秀
score >= 60 分且 score < 85 分,等级为及格。
score < 60 分,等级为不及格。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 create procedure p()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 p();
参数
类型
含义
备注
IN
该类参数作为输入,也就是需要调用时传入值
默认
OUT
该类参数作为输出,也就是该参数可以作为返回值
INOUT
既可以作为输入参数,也可以作为输出参数
用法:
1 2 3 4 CREATE PROCEDURE 存储过程名称 ([IN / OUT / INOUT 参数名 参数类型])BEGIN END ;
案例
(1)根据传入参数 score
,判定当前分数对应的分数等级,并返回。
score >= 85 分,等级为优秀
score >= 60 分且 score < 85 分,等级为及格。
score < 60 分,等级为不及格。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 create procedure p(in score int , out result varchar (10 ))begin if score >= 85 then set result := '优秀' ; elseif score >= 60 then set result := '及格' ; else set result := '不及格' ; end if; select result ;end ;call p(68 , @result );select @result ;
(2)将传入的 200 分制的分数,进行换算,换算成百分制,然后返回。
1 2 3 4 5 6 7 8 create procedure p(inout score double )begin set score := score * 0.5 ;end ;set @score = 78 ;call p(@score );select @score ;
2.7 case
语法一
1 2 3 4 5 CASE case_value WHEN when_value1 THEN statement_list1 [WHEN when_value2 THEN statement_list2]... [ELSE statement_list]END CASE ;
语法二
1 2 3 4 5 CASE WHEN search_condition1 THEN statement_list1 [WHEN search_condition2 THEN statement_list2]... [ELSE statement_list]END CASE ;
案例
根据传入的月份,判定月份所属的季节(需求采用 case
结构)。
1-3 月份,为第一季度
4-6 月份,为第二季度
7-9 月份,为第三季度
10-12 月份,为第四季度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 create procedure p(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 p(4 );
2.8 while
while
循环是有条件的循环控制语句。满足条件后,再执行循环体中的
SQL
语句。具体语法为:
1 2 3 4 WHILE 条件 DO SQL 逻辑...END WHILE;
案例
计算从 1
累加到 n
的值, n
为传入的参数值。
1 2 3 4 5 6 7 8 9 10 11 12 create procedure p(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 p(10 );
2.9 repeat
repeat
是有条件的循环控制语句,当满足条件的时候退出循环。具体语法为:
1 2 3 4 5 REPEAT SQL 逻辑... UNTIL 条件END REPEAT;
案例
计算从 1
累加到 n
的值, n
为传入的参数值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 create procedure p(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 p(10 );
2.10 loop
LOOP
实现简单的循环,如果不在 SQL
逻辑中增加退出循环的条件,可以用来其来实现简单的死循环。
LOOP
可以配合一下两个语句使用:
LEAVE
:配合循环使用,退出循环。
ITERATE
:必须用在循环中,作用是跳过当前循环剩下的语句,直接进入下一次循环。
1 2 3 [begin_label:] LOOP SQL 逻辑...END LOOP [end_label];
1 2 LEAVE label; ITERATE label;
案例
计算从 1
累加到 n
的值, n
为传入的参数值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 create procedure p(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 p(10 );
计算从 1
到 n
之间的偶数累加的值,
n
为传入的参数值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 create procedure p(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 ; iterate sum; end if; set total := total + n; set n := n - 1 ; end loop sum; select total;end ;call p(10 );
2.11 游标
游标( CURSOR
)
是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环的处理。游标的使用包括游标的声明、
OPEN
、 FETCH
和 CLOSE
,其语法分别如下。
声明游标
1 DECLARE 游标名称 CURSOR FOR 查询语句;
打开游标
获取游标记录 1 FETCH 游标名称 INTO 变量[,变量];
关闭游标
案例
使用游标创建一个存储过程,统计 STUDENT
表年龄大于
19
的记录的数量,如下为 STUDENT
表的创建代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 DELIMITER $CREATE PROCEDURE PROC_STAT()BEGIN # 创建 用于接收游标值的变量 DECLARE ID,AGE,TOTAL INT ; DECLARE NAME,SEX CHAR (10 ); # 游标结束的标志 DECLARE DONE INT DEFAULT 0 ; # 声明游标 DECLARE CUR CURSOR FOR SELECT STUID,STUNAME,STUSEX,STUAGE FROM STUDENT WHERE STUAGE > 19 ; # 指定游标循环结束时的返回值 DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = 1 ; # 打开游标 OPEN CUR; # 初始化变量 SET TOTAL = 0 ; # WHILE 循环 WHILE DONE != 1 DO FETCH CUR INTO ID,NAME,SEX,AGE; IF DONE != 1 THEN SET TOTAL = TOTAL + 1 ; END IF; END WHILE; # 关闭游标 CLOSE CUR; # 输出累计的结果 SELECT TOTAL;END $ DELIMITER ; # 调用存储过程CALL PROC_STAT();
2.12 条件处理程序
条件处理程序(Handler)
可以用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤。具体语法为:
1 2 3 4 5 6 7 8 9 10 DECLARE handler_action HANDLER FOR condition_value [,condition_value]... statement; handler_action CONTINUE:继续执行当前程序 EXIT:中止执行当前程序 condition_value SQLSTATE sqlstate_value:状态码,如02000 ,表示抓取的时候没数据 SQLWARNING :所有以01 开头的SQLSTATE 代码的简写 NOT FOUND:所有以02 开头的SQLSTATE 代码的简写 SQLEXCEPTION :所有没有被SQLWARNNING或NOT FOUND捕获的SQLSTATE 代码的简写
案例
根据传入的参数 uage
,来查询用户表 tb_user
中,所有的用户年龄小于等于 uage
的用户姓名(
name
)和专业(
profession
),并将用户的姓名和专业插入到所创建的一张新表(
id,name,profession
) 中。
逻辑:
声明游标,存储查询结果集
准备:创建表结构
开启游标
获取游标中的记录
插入数据到新表中
关闭游标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 create procedure p(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; declare exit handler for SQLSTATE '02000' 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 p(40 );
3. 存储函数
存储函数是有返回值的存储过程,存储函数的参数只能是 IN
类型的。具体语法如下:
1 2 3 4 5 6 CREATE FUNCTION 存储函数名称([参数列表])RETURNS type [characteristic...]BEGIN RETURN ...;END ;
characteristic
说明:
DETERMINISTIC
:相同的输入参数总是产生相同的结果。
NO SQL
:不包含 SQL
语句。
READS SQL DATA
:包含读取数据的语句,但不包含写入数据的语句。
DETERMINISTIC :
表示函数的返回值在给定相同的输入参数时总是相同的。也就是说,对于一组特定的输入参数,函数将始终产生相同的输出。
这个特性对于数据库优化很重要,因为如果一个函数是确定性的,那么在查询计划中,数据库可以缓存这个函数的结果,以提高查询效率。
如果函数内部涉及到随机数生成或者依赖于外部变化的数据(如当前时间、用户会话变量等),则不应该声明为DETERMINISTIC。
NO SQL :
表示函数中不包含任何SQL语句,即函数体中没有对数据库进行任何读写操作。
声明为NO
SQL的函数可以安全地在如事务处理等需要确保ACID属性(原子性、一致性、隔离性、持久性)的上下文中使用,因为它们不会影响数据库的数据。
READS SQL DATA :
表示函数中包含读取数据的SQL语句,但不包含写入数据的语句。
这种类型的函数可能会从数据库中读取数据,但不进行任何修改。因此,它们不会影响数据的一致性和完整性。
声明为READS SQL
DATA的函数可以在大多数上下文中安全使用,但数据库系统会确保这些函数不会在不允许读取数据的上下文中被调用。
案例
计算从 1
累加到 n
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 create function fun1(n int )return 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(50 );
4. 触发器
4.1 介绍
触发器是与表有关的数据库对象,指在 insert/update/delete
之前或之后,触发并执行触发器中定义的 SQL
语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志记录,数据校验等操作。
使用别名 OLD
和 NEW
来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。
触发器类型
NEW 和 OLD
INSERT 型触发器
NEW 表示将要或者已经新增的数据
UPDATE 型触发器
OLD 表示修改之前的数据,NEW 表示将要或已经修改后的数据
DELETE 型触发器
OLD 表示将要或者已经删除的数据
4.2 语法
创建 1 2 3 4 5 6 CREATE TRIGGER trigger_name BEFORE/ AFTER INSERT / UPDATE / DELETE ON tbl_name FOR EACH ROW BEGIN trigger_stmt;END ;
查看
删除 1 DROP TRIGGER [schema_name.]trigger_name;
4.3 案例
通过触发器记录 tu_user
表的数据变更日志,将变更日志插入到日志表 user_logs
中,包含增加、修改和删除。 1 2 3 4 5 6 7 8 9 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;
INSERT
类型触发器:
案例1
编写一个名为 INSERT_S
的触发器,在 S
表执行
INSERT
语句后被激发,此触发器将新供应商的
SNO
、 SNAME
、 STATUS
、
CITY
及执行此操作的用户( USER
)插入
N_S
表, N_S
表比 S
表增添操作用户一列。
1 2 3 4 5 6 delimiter $create trigger INSERT_S after insert on S for each row begin insert into N_S(SNO,SNAME,STATUS,CITY,USER ) VALUES (new.SNO,new.SNAME,new.STATUS,new.CITY,USER ());end $ delimiter ;
案例2
编写一个名为 INSERT_SPJ
的触发器,当 SPJ
表中有插入某条记录时,自动更新表 SPJ_SUMQTY
表。
1 2 3 4 5 6 7 8 9 10 delimiter $create trigger INSERT_SPJ after insert on SPJ for each row begin if exists (select * from SPJ_SUMQTY as s where s.PNO = new.PNO and s.JNO = new.JNO) then update SPJ_SUMQTY as s1 set s1.SUMQTY = s1.SUMQTY + new.QTY where s1.JNO = new.JNO and s1.PNO = new.PNO; else insert into SPJ_SUMQTY VALUES (new.JNO, new.PNO, new.QTY); end if;end $ delimiter ;
案例3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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, concat('插入的数据内容为:id=' ,new.id,',name=' ,new.name,',phone=' ,new.phone,',email=' ,new.email,',profession=' ,new.profession));end ;show triggers;insert into tb_user(id, name, phone, email, profession, age, gender, status, createtime)VALUES (25 ,'二皇子' ,'18809091212' ,'erhuangzi@163.com' ,'软件工程' ,23 ,'1' ,'1' ,now());drop trigger tb_user_insert_trigger;
UPDATE
类型触发器:
案例1
编写一个名为 UPDATE_S
的触发器,检查 S
表的
STATUS
,只允许 0-100
之间,超过
100
后,改为 100
。
1 2 3 4 5 6 7 8 delimiter $create trigger UPDATE_S before update on S for each row begin if new.STATUS > 100 then set new.STATUS = 100 ; end if;end $ delimiter ;
案例2
编写一个名为 UPDATE_SPJ
的触发器,当 SPJ
表中有更新某条记录时,自动更新表 SPJ_SUMQTY
表。
1 2 3 4 5 6 delimiter $create trigger UPDATE_SPJ before update on SPJ for each row begin update SPJ_SUMQTY as s1 set s1.SUMQTY = s1.SUMQTY + new.QTY - old.QTY where s1.JNO = new.JNO and s1.PNO = new.PNO;end $ delimiter ;
案例3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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, concat('更新之前的数据:id=' ,new.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;update tb_user set age = 32 where id = 23 ;
DELETE
类型触发器:
案例1
编写一个名为 DELETE_SPJ
的触发器,当 SPJ
表中有删除某条记录时,自动更新表 SPJ_SUMQTY
表。
1 2 3 4 5 6 7 delimiter $create trigger DELETE_SPJ after delete on SPJ for each row begin update SPJ_SUMQTY as s1 set s1.SUMQTY = s1.SUMQTY - old.QTY where s1.JNO = old.JNO and s1.PNO = old.PNO; delete from SPJ_SUMQTY as s1 where s1.SUMQTY = 0 ;end $ delimiter ;
案例2
1 2 3 4 5 6 7 8 9 10 11 12 13 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, concat('删除之前的数据:id=' ,old.id,',name=' ,old.name,',phone=' ,old.phone,',email=' ,old.email,',profession=' ,old.profession));end ;show triggers;delete from tb_user where id = 25 ;