视图
1 什么是视图
视图就是通过查询得到一张虚拟表,然后保存下来,下次直接使用即可
2 为什么要用视图
当反复用到两张表的连接操作时,可以将联成的表当做视图保存下来,下次直接使用
3 如何用视图
create view teacher2course as
select * from teacher inner join course on teacher .tid = course.teacher_id;
强调
1 在硬盘中,视图只有表结构文件,没有表数据文件
2 视图通常是用于查询,不要修改视图表中的数据
删除视图:drop view teacher2course;
不会 视图是mysql的功能,如果你的项目里面大量的使用到了视图,那意味着你后期想要扩张某个功能的时候这个功能恰巧又需要对视图进行修改,意味着你需要先在mysql这边将视图先修改一下,然后再去应用程序中修改对应的sql语句,这就涉及到跨部门沟通的问题,所以通常不会使用视图,而是通过重新修改sql语句来扩展功能
触发器
在满足对某张表数据的增、删、改的情况下,自动触发的功能称之为触发器
如何触发器
对某一张表数据增insert 、删delete、改update的情况下,自动运行另外一段sql代码
如何使用
创建触发器标准语句结构 增加数据 触发器的名字应该具有描述作用 create trigger 触发器名字 after insert on 表名 for each row begin sql 语句 end 增加数据之后 create trigger tri_after_insert_t1 after insert on t1 for each row begin sql 语句 end 增加数据之前 create trigger tri_befor_insert_t1 befor insert on t1 for each row begin sql 语句 end 删除数据
删除数之前 create trigger tri_before_delete_t1 before delete on t1 for each row
begin
sql语句
end
删除数据之后
create trigger tri_after_delte_t1 agter delete on t1 for each row
begin
sql语句
end
修改数据
修改数据之前
create trigger tri_before_update_t1 before update on t1 for each row
begin
sql语句
end
修改数据之后
create trigger tri_after_update_t1 after update on t1 for each row
begin
sql语句
end
举例 create table cmd( id int primary key auto_increment, user char(32), priv char(10), cmd char(64), sub_time datetime ,#提交时间 success enum('yes','no') #0 带帽执行失败 ); create table errlog( id int primary key auto_increment, err_cmd char(64), err_time datetime); #创建触发器 delimiter $$ #将mysql默认的结束符由 ; 改成$$ create trigger tri_after_insert_cmd after insert on cmd for each row begin if NEW.success='no' then #新纪录都会被mysql封装成NEW对象 insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time); end if; end $$ delimiter ; #结束后将mysql结束符换回来 不然后面结束符就都是$$了 insert into cmd(user, priv,cmd,sub_time,success) values( ('egon','0755','ls -l /etc',NOW(),'yes'), ('egon','0755','cat /etc/passwd',NOW(),'no'), ('egon','0755','useradd xxx',NOW(),'no'), ('egon','0755','ps aux',NOW(),'yes'); #查询errlog表记录
select * from errlog; #删除触发器 drop trigger tri_after_insert_cmd;
事务
什么是事物
开启一个事务可以包含一些sql语句,这些sql语句要么同时成功
要么一个都别想成功,称之为事物的原子性
事物的作用
保证了对数据操作的数据安全性
案例:用交行的卡操作建行atm机给工商的账户转钱
事务应具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性
原子性(atomicity):一个事务是不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事物内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability):持久性也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
如何用
create table user( id int primary key auto_increment, name char(32), balance int);
insert into user(name,balance) values('wsb',1000),('egon',1000),('ysb',1000); #修改数据之前先开启事务操作 start transaction; #修改操作 update user set balance=900 where name ='wsb'; #买支付100元 update user set balance=1010 where name='egon'; #中介拿走10元 update user set balance=1090 where name='ysb'; #卖家拿到90元
#回滚到上一个状态
rollback;
#开启事务之后,只要没有执行commit操作,数据其实都没有正则刷新到硬盘
commit;
开启事务检测操作是否完整,不完整主动回滚到上一个状态,如果完整就应该执行commit操作
站在python代码的角度,应该实现的伪代码逻辑,
try:
update user set balance=900 where name='wsb'; #买支付100元
update user set balance=1010 where name='egon'; #中介拿走10元
update user set balance=1090 where name='ysb'; #卖家拿到90元
except 异常:
rollbalce;
else:
commit;
检测异常用到存储过程?
存储过程
存储过程包含了一系列可执行的sql语句,存储过程存放于mysql中,通过调用它的名字可以执行其内部的一堆sql
三种开发模型:
第一种: 应用程序:只需要开发应用程序的逻辑 mysql:编写好存储过程,以供应用程序调用 优点:开发效率,执行效率都高 缺点:考虑到人为因素、跨部门沟通等问题,会导致扩展性差 第二种: 应用程序:除了开发应用程序的逻辑,还需要编写原生sql 优点:比方式1,扩展性高(非技术性的) 缺点: 1 开发效率,执行难效率都不如方式1 2 编写原生sql太过于复杂,而且需要考虑带sql语句的优化问题 第三种: 应用程序:开发应用程序的逻辑,不需要编写原生sql,基于别人编写好的框架来处理数据,ORM 优点:不用再编写纯生sql,这意味着开发效率比方式2高,同时兼容方式2扩展性高的好处 缺点:执行效率连方式2都比不过
创建存储过程
delimiter $$ create procedure p1( in m int, #in表示这个参数必须只能是传入不能返回出去 in n int , out res int #out表示这个参数可以被返回出去,还有一个inout表示既可以传入也可以被返回出去) begin select tname from teacher where tid >m and tid <n; set res=0; end $$ delimiter; call p1() ; #调用存储过程 drop procedure p1; #删除存储过程
如何用存储过程
#前提:存储过程在哪个库下面创建的只能在对应的库下面才能使用!!
1 直接在mysql 中调用 set @res=10 #res的值是用来判断存储过程是否被执行成功的依据,所以需要先定义一个变量@res存储10 变量必须用@声明 call p1(2,4,10); #报错 call p1(2,4,@res); #查看结果 select @res; #执行成功,@res变量值发生了变化 2 在python程序中调用 pymysql连接mysql 产生的游标cursor.callproc('p1',(2,4,10)) #内部原理:@_p1_0=2,@_p1_1=4,@_p1_2=10; cursor.execute('select @_p1_2') print(cursor.fetchall())
#介绍 delimiter // create procedure p4( out status int ) BEGIN 1. 声明如果出现异常则执行{ set status = 1; rollback; } 开始事务 -- 由秦兵账户减去100 -- 方少伟账户加90 -- 张根账户加10 commit; 结束 set status = 2; END // delimiter ; #实现 delimiter // create PROCEDURE p5( OUT p_return_code tinyint ) BEGIN DECLARE exit handler for sqlexception BEGIN -- ERROR set p_return_code = 1; rollback; END; DECLARE exit handler for sqlwarning BEGIN -- WARNING set p_return_code = 2; rollback; END; START TRANSACTION; #事务 DELETE from tb1; #执行失败 insert into blog(name,sub_time) values('yyy',now()); COMMIT; -- SUCCESS set p_return_code = 0; #0代表执行成功 END // delimiter ; #在mysql中调用存储过程 set @res=123; call p5(@res); select @res; #在python中基于pymysql调用存储过程 cursor.callproc('p5',(123,)) print(cursor.fetchall()) #查询select的查询结果 cursor.execute('select @_p5_0;') print(cursor.fetchall())
函数
#date_format 1基本使用 select date_format('2009-10-04 22:23:00','%W %M %Y'); Sunday October 2009 select date_format('2009-10-04 22:23:00','%H:%i:%s'); 22:23:00 select date_format('1900-10-04 22:23:00','%D %y %a %d %m %b %j'); 4th 00 Thu 04 10 Oct 277 select date_format('1997-10-04 22:23:00','%H %k %I %r %T %S %w'); 22 22 10 10:23:00 PM 22:23:00 00 6 select date_format('1999-01-01','%X %V'); 1998 52 select date_format('2006-06-00','%d'); 00
2 准备表和记录
create table blog(
id int primary key auto_incremate,
name char(32),
sub_time datetime);
insert into blog (name,sub_time) values
('第1篇','2015-03-01 11:31:21'), ('第2篇','2015-03-11 16:31:21'), ('第3篇','2016-07-01 10:21:31'), ('第4篇','2016-07-22 09:23:21'), ('第5篇','2016-07-23 10:11:11'), ('第6篇','2016-07-25 11:21:31'), ('第7篇','2017-03-01 15:33:21'), ('第8篇','2017-03-01 17:32:21'), ('第9篇','2017-03-01 18:31:21');
3 提取sub_time 字段的值,按照格式后的结果及‘年月’来分组
select date_format(sub_time,'%Y-%m'),count(1) form blog group by date_format(sub_time,'%Y_%m');
#结果 +-------------------------------+----------+ | DATE_FORMAT(sub_time,'%Y-%m') | COUNT(1) | +-------------------------------+----------+ | 2015-03 | 2 | | 2016-07 | 4 | | 2017-03 | 3 | +-------------------------------+----------+ 3 rows in set (0.00 sec)
流程控制
if条件语句
delimiter // create procedure proc_if() begin declare i int default 0; if i =1 then select 1; elseif i =2 then select 2; else select 7; end if; end // delimiter ;
while 循环
delimiter // create procedure pro_while() begin declare num int; set num=0; while num<10 DO select num; set num=num+1; end while; end // delimiter ;
索引与满查询优化
数据都是存在硬盘上的,那查询数据不可避免的需要进行io操作
索引在mySQL中也叫作‘键’ ,是存储引擎用于快速找到记录的一种数据结构。
primary key unique key index key
注意:foreign key 不是用来加速查询用的,上面三种key前两种除了有加速查询的效果之外还有额外的约束条件(primary key:非空且唯一, unique key:唯一),而index key 没有任何约束功能只会帮你加速查询
索引就是一种数据结构,类似于书的目录。意味着以后在查数据应该先查目录再找数据,而不是用翻页的方式查询数据
本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事情变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查询方式来锁定数据。
索引的影响:
在表中有大量数据的前提下,创建索引速度回很慢
在索引创建完毕后,对表的查询性能会大幅度提升,但是写的性能会降低
b+树
只有叶子结点存放真实数据,跟和树枝节点存的仅仅是虚拟数据结构
查询次数由树的层级决定,层级越低次数越少
一个磁盘块儿的大小是一定的,那也就意味着能存的数据量是一定的。如何保证树的层级最低呢?一个磁盘块儿存放占用空间比较小的数据项
思考我们应该给我们一张表里面的什么字段字段建立索引能够降低树的层级高度>>> 主键id字段
聚集索引(primary key)
特点:叶子结点放的一条条完整的记录
聚集索引其实指的就是表的主键,innodb引擎规定一张表中必须要有主键。先来回顾一下存储引擎。
myisam在建表的时候对应到硬盘有几个文件(三个)?
innodb在建表的时候对应到硬盘有几个文件(两个)?frm文件只存放表结构,不可能放索引,也就意味着innodb的索引跟数据都放在idb表数据文件中。
辅助索引(unique,index) 覆盖索引 :命中 非覆盖索引:利用主键去查
辅助索引:查询数据的时候不可能都是用id作为筛选条件的,也可能会用name,age ,password 等字段信息,那么这个时候就无法利用到聚集索引的加速查询效果。就需要给其他字段建立索引,这些索引就是辅助索引
特点:叶子结点存放的是辅助索引字段对应的那条记录的主键的值(比如:按照name字段创建索引,那么叶子结点存放的是:{name对应的值:name所在的那条记录的主键值})
select name from user where name='jason'; 上述语句叫覆盖索引:只在辅助索引的叶子节点中就已经找到了所有我们想要的数据 select age from user where name='jason'; 上述语句叫非覆盖索引,虽然查询的时候命中了索引字段name,但是要查的是age字段,所以还需要利用主键才去查找
#1. 准备表 create table s1( id int, name varchar(20), gender char(6), email varchar(50) ); #2. 创建存储过程,实现批量插入记录 delimiter $$ #声明存储过程的结束符号为$$ create procedure auto_insert1() BEGIN declare i int default 1; while(i<3000000)do insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy')); set i=i+1; end while; END$$ #$$结束 delimiter ; #重新声明 分号为结束符号 #3. 查看存储过程 show create procedure auto_insert1\G #4. 调用存储过程 call auto_insert1(); # 表没有任何索引的情况下 select * from s1 where id=30000; # 避免打印带来的时间损耗 select count(id) from s1 where id = 30000; select count(id) from s1 where id = 1; # 给id做一个主键 alter table s1 add primary key(id); # 速度很慢 select count(id) from s1 where id = 1; # 速度相较于未建索引之前两者差着数量级 select count(id) from s1 where name = 'jason' # 速度仍然很慢 """ 范围问题 """ # 并不是加了索引,以后查询的时候按照这个字段速度就一定快 select count(id) from s1 where id > 1; # 速度相较于id = 1慢了很多 select count(id) from s1 where id >1 and id < 3; select count(id) from s1 where id > 1 and id < 10000; select count(id) from s1 where id != 3; alter table s1 drop primary key; # 删除主键 单独再来研究name字段 select count(id) from s1 where name = 'jason'; # 又慢了 create index idx_name on s1(name); # 给s1表的name字段创建索引 select count(id) from s1 where name = 'jason' # 仍然很慢!!! """ 再来看b+树的原理,数据需要区分度比较高,而我们这张表全是jason,根本无法区分 那这个树其实就建成了“一根棍子” """ select count(id) from s1 where name = 'xxx'; # 这个会很快,我就是一根棍,第一个不匹配直接不需要再往下走了 select count(id) from s1 where name like 'xxx'; select count(id) from s1 where name like 'xxx%'; select count(id) from s1 where name like '%xxx'; # 慢 最左匹配特性 # 区分度低的字段不能建索引 drop index idx_name on s1; # 给id字段建普通的索引 create index idx_id on s1(id); select count(id) from s1 where id = 3; # 快了 select count(id) from s1 where id*12 = 3; # 慢了 索引的字段一定不要参与计算 drop index idx_id on s1; select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 针对上面这种连续多个and的操作,mysql会从左到右先找区分度比较高的索引字段,先将整体范围降下来再去比较其他条件 create index idx_name on s1(name); select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 并没有加速 drop index idx_name on s1; # 给name,gender这种区分度不高的字段加上索引并不难加快查询速度 create index idx_id on s1(id); select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 快了 先通过id已经讲数据快速锁定成了一条了 select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 慢了 基于id查出来的数据仍然很多,然后还要去比较其他字段 drop index idx_id on s1 create index idx_email on s1(email); select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 快 通过email字段一剑封喉
联合索引
select count(id) form s1 where name='jason' and gender ='male' and id>3 and email ='xxx'; #如果上述四个字段区分度很高,那给谁都能加速查询 #给email 加 然而不用email字段 select count(id) from s1 where name='jason' and gender='male' and id >3; #给name 加然后不用name字段 select count(id) from s1 where gender ='male' and id >3; #给gender加 然后不用gender字段 select count(id) from s1 where id >3; #带来的问题是所有的字段都建了索引然后都没有用到,还需要花费四次建立的时间 create index idx_all on s1(email,name,gender,id) ; # 最左匹配原则,区分度高的往左放 select count(id) from s1 where name='jason' and gender='male' and id >3 and email='xxx'; #速度变快