MySQL 视图 触发器 存储过程 函数 流程控制 索引与慢查询优化

视图

视图就是一张虚拟表,虚拟表都是通过查询得到的

使用视图我们可以把查询过程中的临时表摘出来,用视图去实现,这样以后再想操作该临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,但视图有明显地效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推荐使用

强调:
1.在硬盘中虚拟表只有 .frm文件(表结构),没有 .idb文件(数据)
2.使用视图以后就无需每次都重写子查询的sql,但是这么效率并不高,还不如我们写子查询的效率高
3.视图通常用来辅助查询,不要修改视图中的数据!!!

案例表

create table course(
  cid int primary key auto_increment,
  cname char(20),
  teacher_id int
);
create table teacher(
  tid int primary key auto_increment,
  tname char(20)
);

insert into course(cname, teacher_id) values
    ('生物',1),
    ('物理',2),
    ('体育',3),
    ('美术',2);

insert into teacher(tname) values 
  ('张磊老师'),
  ('李平老师'),
  ('刘海燕老师'),
  ('朱云海老师'),
  ('李杰老师');

 select * from course;
+-----+--------+------------+
| cid | cname  | teacher_id |
+-----+--------+------------+
|   1 | 生物   |          1 |
|   2 | 物理   |          2 |
|   3 | 体育   |          3 |
|   4 | 美术   |          2 |
+-----+--------+------------+

select * from teacher;
+-----+-----------------+
| tid | tname           |
+-----+-----------------+
|   1 | 张磊老师        |
|   2 | 李平老师        |
|   3 | 刘海燕老师      |
|   4 | 朱云海老师      |
|   5 | 李杰老师        |
+-----+-----------------+
  • 创建视图

创建一张老师与课程内连接的虚拟表
create view course_teacher as
    select * from course inner join teacher on course.teacher_id = teacher.tid;

select * from course_teacher;
+-----+--------+------------+-----+-----------------+
| cid | cname  | teacher_id | tid | tname           |
+-----+--------+------------+-----+-----------------+
|   1 | 生物   |          1 |   1 | 张磊老师        |
|   2 | 物理   |          2 |   2 | 李平老师        |
|   3 | 体育   |          3 |   3 | 刘海燕老师      |
|   4 | 美术   |          2 |   2 | 李平老师        |
+-----+--------+------------+-----+-----------------+
  • 修改视图

ALTER VIEW 视图名称 AS SQL语句
  • 删除视图 

DROP VIEW 视图名称

触发器

在满足对某张表数据 增删改 的情况下,自动触发的功能称之为触发器(一种监测机制)

1.为何要用触发器
触发器专门针对我们对某一张表数据 增insert,删delete,改update 的行为,着类行为一旦执行,就会触发触发器的执行,即自动运行另外一段sql代码

创建触发器语法:
# 针对增
create trigger tri_after_insert_ti after insert on 表名 for each row
begin
    sql代码...
end
create trigger tri_before_insert_ti before insert on 表名 for each row
begin
    sql代码...
end
​
# 针对删
create trigger tri_after_delete_t1 after delete on 表名 for each row
begin
    sql代码。。。
end
create trigger tri_after_delete_t2 before delete on 表名 for each row
begin
    sql代码。。。
end
​
# 针对改
create trigger tri_after_update_t1 after update on 表名 for each row
begin
    sql代码。。。
end
create trigger tri_after_update_t2 before update on 表名 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 ;  # 结束之后记得再改回来,不然后面结束符就都是$$了
​
#往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志
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语句,存储过程放于MySQL中,通过调用它的名字可以执行其内部的一堆sql(像在对象里封装了一堆方法,直接用对象调用方法即可)

存储过程在哪个库下创建的只能在对应的库下面才能使用

优点

1.用于替代程序写的sql语句,实现程序与sql解耦

2.给予网络传输,传别名的数据量小,而直接传sql数据量大

缺点

扫描二维码关注公众号,回复: 8901999 查看本文章

1.程序员扩展功能不方便

创建存储过程 procedure

create procedure 名(参数)
begin
    sql语句
end

使用存储过程

  • 无参数
delimiter $$
create procedure p1()
begin
    select * from user;
end $$

delimiter ;

#在mysql中调用
call p1() 

 call p1();
+----+---------+----------+
| id | name    | password |
+----+---------+----------+
|  1 | xiongda | 123      |
|  2 | xionger | 456      |
|  3 | xxx     | 789      |
+----+---------+----------+
  • 有参数

in 用于传入参数使用

out 用于返回值用

inout 既可以传入也可以当做返回值

delimiter $$
create procedure p2(
    in n int,
    in m int,
    out res int
)
begin
    select * from user where id > n and id < m;
    set res=0;
end $$

delimiter ;

#在mysql中调用
set @res=0; #0代表假(执行失败),1代表真(执行成功)
call p2(1, 3, @res);  # 注意:返回值只能接收变量,所以要先定义变量再传入
select @res;

set @res=0; 

call p2(1, 3, @res);
+----+---------+----------+
| id | name    | password |
+----+---------+----------+
|  2 | xionger | 456      |
+----+---------+----------+

select @res;
+------+
| @res |
+------+
|    0 |
+------+

python中调用存储过程 callproc

python中基于pymsql调用存储过程

游标.callproc(存储过程名, (参数们))

cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.callproc('p2',(1, 3, 1))  # @_p2_0=1,@_p2_1=3,@_p2_2=1,
print(cursor.fetchall())  # [{'id': 2, 'name': 'xionger', 'password': '456'}]

cursor.execute('select @_p2_2;')
print(cursor.fetchall())  # [{'@_p2_2': 0}]

查看存储过程

show create procedure 名;

删除存储过程

drop function 名;

函数

注意与存储过程的区别,mysql内置的函数只能在sql语句中使用!

参考博客:http://www.cnblogs.com/linhaifeng/articles/7495918.html#_label2

CREATE TABLE blog (
    id INT PRIMARY KEY auto_increment,
    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');
​
select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');

流程控制

# 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 proc_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+树

https://images2017.cnblogs.com/blog/1036857/201709/1036857-20170912011123500-158121126.png

只有叶子结点存放真实数据,根和树枝节点存的仅仅是虚拟数据

查询次数由树的层级决定,层级越低次数越少

一个磁盘块儿的大小是一定的,那也就意味着能存的数据量是一定的。如何保证树的层级最低呢?一个磁盘块儿存放占用空间比较小的数据项

思考我们应该给我们一张表里面的什么字段字段建立索引能够降低树的层级高度>>> 主键id字段

聚集索引(primary key)

聚集索引其实指的就是表的主键,innodb引擎规定一张表中必须要有主键。先来回顾一下存储引擎。

myisam在建表的时候对应到硬盘有几个文件(三个)?

innodb在建表的时候对应到硬盘有几个文件(两个)?frm文件只存放表结构,不可能放索引,也就意味着innodb的索引跟数据都放在idb表数据文件中。

特点:叶子结点放的一条条完整的记录

辅助索引(unique,index)

辅助索引:查询数据的时候不可能都是用id作为筛选条件,也可能会用name,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) from 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';  # 速度变快

mysql中存在一种专门的数据结构,key,又叫索引,通过该数据结构可以不断的缩小查询范围从而减少IO次数,达到加速查询效率目的
- index key : 只加速查询效果,没有约束功能
- unique key : 不仅有加速查询效果,还附加了约束功能
- primary key : 不仅有加速查询效果,还附加了约束功能,并且innodb存储引擎会按照主键字段的值来组织表中所有的数据,所以一张innodb表中有且只有一个主键,innodb的索引跟数据都放在idb表数据文件中

  •  索引的影响

当表中有数据再建索引,建索引的速度会慢,因为要扫描数据进行'归类'

当存在索引再插入数据,插数据的速度会慢,因为之前的索引结构需要重新编排.

所以索引可以加速查询,但会影响写入数据速度

  • 聚集索引(primary key)

聚集索引其实指的就是表的主键

特点:叶子结点放的一条条完整的记录

  • 辅助索引(unique,index)

只在辅助索引的叶子节点就已经找到了我们想要的数据

特点:叶子结点存放的是辅助索引字段对应的那条记录的主键的值(比如:按照name字段创建索引,那么叶子节点存放的是:{name对应的值:name所在的那条记录的主键值})

select name from user where name='xionger';

上述语句叫覆盖索引:只在辅助索引的叶子节点中就已经找到了所有我们想要的数据

select age from user where name='xionger';

上述语句叫非覆盖索引,虽然查询的时候命中了索引字段name,但是要查的是age字段,所以还需要利用主键才去查找

发布了163 篇原创文章 · 获赞 4 · 访问量 1782

猜你喜欢

转载自blog.csdn.net/Waller_/article/details/102431914