MySQL进阶- SQL优化和视图

目录

  • SQL优化
    • 插入数据时的SQL优化(insert优化,和大批量数据插入)
    • 主键优化
    • order by优化(排序操作的优化)
    • group by优化(分组优化)
    • limit优化(分页查询优化)
    • count语句的优化
    • update语句的优化
    • 小结
  • 视图
    • 视图的介绍
    • 视图的基本语法
    • 视图的检查选项
    • 视图的更新
    • 视图的作用
    • 视图的案例

SQL优化

插入数据时的SQL优化(insert优化,和大批量数据插入)

  • 批量插入
    在插入多条数据时,不要一句一句的SQL语句插入,而是一次性插入多条数据

    INSERT INTO 表名  (字段名1,字段名2...) VALUES (值一,值二,...),(值一,值二,...),(值一,值二,...);``
    INSERT INTO 表名 VALUES (值一,值二,...),(值一,值二,...),(值一,值二,...);``
    

    但是批量插入一次最多不要超过1000条,大概就是500到1000条,如果一次性要插入几万条数据,那么可以将其分为多条insert语句插入

  • 手动提交事务
    可以优化插入速度,在插入前手动开启事务,插入完成后手动结束事务

    start transaction;
    insert into 表名 values(具体数据1);
    insert into 表名 values(具体数据2);
    insert into 表名 values(具体数据3);
    ...
    commit;
    
  • 主键顺序插入
    在插入主键时按照主键的顺序插入
    在这里插入图片描述

  • 大批量插入数据
    如果一次性插入大批量数据(万级的),使用insert语句插入性能较低,此时可以使用MySQL提供的load指令插入,通过load指令可以将本地磁盘中的数据全部加载到数据库当中
    MySQL进阶- SQL优化和视图_第1张图片
    使用load指令的步骤:
    1.在客户端连接服务端的时候,加上参数 – local-infile

    mysql--local-infile -u root -p
    

    2.设置全局参数local_infile为1,开启从本地加载文件导入数据的开关

    set global local infile=1;
    

    3.执行load指令将准备好的数据,加载到表结构中

    -- 这里的'/root/sql1.log'是本地文件
    -- fields teminated by ',':表示每一个字段之间使用 , 分割
    -- lines teminated by '\n';表示每一行数据之间使用 \n 分割
    load data local infile '本地文件' into table '表名' fields teminated by ',' lines teminated by '\n';
    

    在这里插入图片描述

    可以看到,默认情况下local_infile这个全局变量是关闭的
    MySQL进阶- SQL优化和视图_第2张图片

    MySQL进阶- SQL优化和视图_第3张图片
    注意这里插入数据时,由于是虚拟机,所以要现在finalshall中上传数据,把数据上传到虚拟机中
    在这里插入图片描述
    这里插入100万条数据只需要耗时16秒,很强

    使用load插入时也需要主键顺序插入,顺序插入数据高于乱序插入

    主键顺序插入性能高于乱序插入

主键优化

  • InnoDB中数据的组织方式:

    在InnoDB中,表的数据都是根据主键的顺序组织存放的,这种存储方式被称为索引组织表。(即每行数据在页中都是顺序存放)
    MySQL进阶- SQL优化和视图_第4张图片
    page(页)是innoDB磁盘管理的最小单元,一个extent(区)中可以包含64个页

  • 页分裂
    页可以为空,也可以填充一半,也可以全部填满,但是一个页中最少包含2行数据,如果某行的数据较大,超出了页的阈值之后,就会出现行溢出的现象,

    如果顺序插入,就不会出现页分裂,乱序插入就会出现页分裂的现象,导致插入时要多操作页,自然插入的时间就会变长

  • 页删除
    在InnoDB中当删除一行数据时,实际上数据并没有被物理删除,而是数据被标记为删除并且他的空间变的允许其他数据使用

    MySQL进阶- SQL优化和视图_第5张图片

    上图的13,14,15,16就是打上删除的标记了

    当页中删除的数据达到一个阈值(MERGE_THRESHOLD)时(默认为页的50%),innoDB会开始寻找最靠近的页(前或后面的页)看看是否可以将两个页进行合并以优化空间使用

    达到阈值在这里插入图片描述
    然后合并
    在这里插入图片描述
    再插入数据时就会往下一个页中插入数据
    这里阈值(MERGE_THRESHOLD)可以自己设置,默认为50%

  • 主键的设计原则
    满足业务需求的情况下,尽量降低主键的长度

插入数据时,尽量使用顺序插入,尽量使用auto_increment自增主键

尽量不要使用UUID做主键或者其他自然主键(如身份证号)

业务操作时,尽量避免对主键的修改

order by优化(排序操作的优化)

MySQL中的两种排序方式
在这里插入图片描述
using index的效率较高

-- 此时age和phone都没有索引,使用order by排序时都是Using filesort,效率较低
mysql> explain select id,age ,phone from tb_user_s1 order by age;
+----------------+
 | Extra          |
+----------------+
| Using filesort |
+----------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select id,age ,phone from tb_user_s1 order by age,phone;
+----------------+
 | Extra          |
+----------------+
| Using filesort |
-+----------------+
1 row in set, 1 warning (0.00 sec)

-- 为age和phone建立联合索引后,就会变为Using index

-- 若在查询时,order by之后的联合索引的排序不同,例如,一个顺序,一个倒序,
-- 也会出现Using filesort的情况
-- 这里age升序排列,phone倒序排列
select id,age ,phone from tb_user_s1 order by age asc,phone desc;

-- 这种情况的解决方式就是在创建联合索引时就把顺序定好
-- 下面就是在创建联合索引时就确定age和phone的排列方式
create index idx_user_age_phone_ad on tb_user_s1(age asc ,phone desc)

MySQL进阶- SQL优化和视图_第6张图片
MySQL进阶- SQL优化和视图_第7张图片

注意:上述的所有排序优化都有一个条件,就是覆盖索引,如果不是覆盖索引就不行

  • 即order by优化主要就以下几点:
  1. 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则
  2. 尽量使用覆盖索引
  3. 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则
  4. 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认为256k)

group by优化(分组优化)

在分组操作时,建立适当的索引来提升效率
MySQL进阶- SQL优化和视图_第8张图片
没有索引时,直接分组效率是较低的,使用索引对分组进行优化的话,就尽量使用联合索引,注意联合索引的最左前缀法则

在分组操作时,索引的使用也是需要满足最左前缀法则的

limit优化(分页查询优化)

limit一个常见的问题就是,在大数据量的情况下,越往后查询数据,limit的效率月底,例如limit 2000000,10,此时需要mysql排序前2000010记录,但是仅仅返回2000000和2000010之间的记录,其他的记录丢弃,查询排序的代价非常大

官方给出的优化方式是,通过覆盖索引和子查询的方式优化

-- 直接使用limit查询,效率很低,大概要19秒多
select *from tb_user_s2 limit 9000000,10;

-- 使用覆盖索引和子查询的方式优化
-- 先在子查询中找到对应的主键,然后再使用主键进行查询数据
-- 但是这种方式好像在mysql8.0.26语法不支持
select * from tb_user_s2 where id in(select id from tb_user_s2 order by in limit 9000000,10);

-- 那么可以使用另外的语法实现这个效果
-- 把select id from tb_user_s2 order by in limit 9000000,1
-- 返回的结果看成一张表,然后使用多表查询
select s.* from tb_user_s2 s, (select id from tb_user_s2 order by in limit 9000000,10) a where a.id=s.id;
-- 这样写大概查询是10秒,提高9秒的效率

count语句的优化

当数据量很大时,count一次也是很耗时的,
在这里插入图片描述
这里MyISAM的前提条件是没有where条件,如果有where条件,那么MyISAM也会比较慢
在这里插入图片描述
对于count来说,优化策略就是自己计数,在插入或删除数据时自己记录下来

count是一个聚合函数,对于返回的结果集,一行行的判断,如果count函数的参数不是NULL,累计值就加1,否则不加,最后返回累计值,即count参数会统计数据的数量

  • count的用法:
    1.count(*)

    2.count(主键)

    3.count(字段)

    4.count(1)

  • 用法的演示:

-- count(*)就是求取某个表中数据的总体数量
mysql> select count(*)from tb_user_s1;
+----------+
| count(*) |
+----------+
|       16 |
+----------+
1 row in set (0.00 sec)

-- count(字段),会统计字段的数量,对字段里的值统计前会先判断是不是null,
-- 不是null才会统计进去,是null就不会统计
mysql> select count(profession)from tb_user_s1;
+----------+
| count(*) |
+----------+
|       16 |
+----------+
1 row in set (0.00 sec)

-- count(1)的使用和count(*)是一样的,也是会统计数据的总体数量

count(主键)的话,InnoDB会遍历整张表,将所有主键都拿出来,然后累加返回具体数量

count(字段),没有not null约束的话,InnoDB会遍历整张表把字段值拿出来,然后判断每个值是否为null,不为null,数量增加,为null,数量不增加,有not null约束的话,就跟count(主键)类似,InnoDB会遍历整张表把字段值拿出来,然后累加获得字段值的数量

count(1),InnoDB遍历整张表,不取值,对每一行放一个数字1进去,直接按行进行累加

count(*),InnoDB并不会把全部字段值取出来,已经优化过了,不取值,直接按行累加

按照效率的话
就是:
在这里插入图片描述

update语句的优化

需要规避的一个问题是:在更新字段时需要根据带有索引的字段进行更新,即where后的条件字段尽量是带有索引的,否则,就会出现行锁升级为表锁,锁住整张表,从而使并发性能降低

InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁

小结

MySQL进阶- SQL优化和视图_第9张图片
对SQL优化绝大时候都是对索引优化

视图

视图的介绍

视图(View)是一种虚拟存在的表,视图中的数据并不在数据库中真实存在,视图中行和列数据都来自于定义视图的查询中使用的表,并且是在使用视图时动态生成的。

定义视图时使用的表也被叫做基表

简单来说,视图不保存数据,它里面只保存了查询的SQL逻辑,不保存查询结果,所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上

视图的基本语法

  • 创建视图
-- 这里的select语句就是视图所封装的数据 ,select所查询的表就叫做视图的基表
CREATE [OR REPLACE] VIEW 视图名称[(列名列表)] AS SELECT 语句 
[WITH[CASCADED | LOCAL]CHECK OPTION]

create [or replace] view 视图名称[(列名列表)] as SELECT 语句 
[with[cascaded | local]check option]
  • 查询视图
查看创建视图的语句:SHOW CREATE VIEW 视图名称;
查看视图数据:SELECT *FROM 视图名称.....;(就是传统的select查表语句)
  • 修改视图
-- 这里修改的方式1与创建的不同就是,必须加上or replace
方式1CREATE OR REPLACE VIEW 视图名称[(列名列表)]  AS SELECT 语句
[WITH[CASCADED | LOCAL]CHECK OPTION] 
create [or replace] view 视图名称[(列名列表)] as SELECT 语句 
[with[cascaded | local]check option]

方式2ALTER VIEW 视图名称[(列名列表)]  AS SELECT 语句
[WITH[CASCADED | LOCAL]CHECK OPTION]
alter view 视图名称[(列名列表)] as SELECT 语句 
[with[cascaded | local]check option]
  • 删除视图
DROP VIEW [IF EXISTS] 视图名称[,视图名称];
-- 创建视图
create or replace view name_v_1 as select id,name from tb_user_s1 where id<=10;
-- 查询视图
show create view name_v_1;
select*from name_v_1 ;

-- 修改视图
create or replace view name_v_1 as select id,name,age from tb_user_s1 where id<=10;
alter view name_v_1 as select id,age from tb_user_s1 where id<=10;

-- 删除视图
drop view if exists name_v_1;

视图的检查选项

注意:视图在数据库中并不存在,虽然可以往视图里添加数据,但是实际上数据是被添加到视图所对应的基表中去了,并且视图只会显示符合创建条件时的数据,超出条件范围的数据不会视图中,即在视图中找不到超出创建视图条件的数据

-- 创建视图
create or replace view name_v_1 as select id,name from tb_user_s1 
where id<=18;
-- 往视图中插入数据实际上就是往视图所对应的基表中插入数据
-- 这里所对应的基表就是tb_user_s1 
insert into name_v_1 values (17,'Tom');
insert into name_v_1 values (20,'Jack');
-- 上面的两条数据只有(17,'Tom');能在视图中显示出来,因为(20,'Jack');超出了
-- 创建视图的条件

为了避免上面那种情况的发生,可以在创建视图的时候在后面加上
with cascaded check option / with local check option,这样就不会出现超出条件的数据了,因为只要有数据插入时超出条件,就会报错

create or replace view name_v_1 as select id,name from tb_user_s1 
where id<=18 with cascaded check option;


-- 往视图中插入数据实际上就是往视图所对应的基表中插入数据
insert into name_v_1 values (17,'Tom');

-- 此时运行这里就会报错
insert into name_v_1 values (20,'Jack');

上面的with cascaded check option就是视图中检查选项的应用

  • 视图的检查选项
    定义

在这里插入图片描述
即在创建视图时使用with … check option的话,mysql就会检查视图中正在改变的每一行的数据,如果有数据不满足创建视图时的条件,那么就会报错,不允许视图改变,这里默认值就是 cascaded 不加也可以

  • cascaded与local的区别:

    cascaded:作用
    创建视图时(这里新创建的视图记作s1),视图的基表可以是另一个视图(另一个视图记作s2),如果创建时使用cascaded,那么这个新创建的视图(s1)里的数据既要满足s1的条件,又要满足s2的条件,并且,s2在创建时并不一定有with ... check option语句,

    如果以s2为基表又创建一个s3的话,s3在创建时没加with ... check option语句的话,s3里的数据就需要满足s1和s2的条件,不一定需要满足s3的条件(因为s3没有加with ... check option语句),如果s3中也添加了with ... check option语句,那么就需要同时满足s1,s2,s3的条件
    例如:

    -- 这里name_v_1就是s2,name_v_2就是s1,
    -- 那么,添加在s1的数据既要满足id<=18,又要满足id>10
    create or replace view name_v_1 as select id,name from tb_user_s1 
    where id<=18 ;
    create or replace view name_v_2 as select id,name from name_v_1 
    where id>10 with cascaded check option ;
    -- 这条数据满足两个条件,可以插入
    insert into name_v_1 values (17,'Tom');
    
    -- 此时运行这里就会报错,因为不满足s2的条件
    insert into name_v_1 values (20,'Jack');
    

    local:作用
    同样的,创建视图时(这里新创建的视图记作s1),视图的基表可以是另一个视图(另一个视图记作s2),如果创建时使用local,那么这个新创建的视图(s1)里的数据要满足s1的条件,然后看s2中有没有with ... check option语句,如果有的话,就既要满足s1,又要满足s2,如果没有,那么就不用管基表(s2)的条件,只满足s1的条件即可。

    至于这里再以s2作为基表创建新视图s3的规则就跟上面一样了,也满足local的规则
    例如:

    -- 这里name_v_1就是s2,name_v_2就是s1,
    -- 那么,添加在s1的数据就要满足id>10,
    -- 由于s2中没有``with ... check option``语句,所以,不用满足s2的条件
    create or replace view name_v_1 as select id,name from tb_user_s1 
    where id<=18 ;
    create or replace view name_v_2 as select id,name from name_v_1 
    where id>10 with cascaded check option ;
    
    -- 这条数据满足s1条件,可以插入
    insert into name_v_1 values (17,'Tom');
    
    -- 这条数据也满足s1条件,可以插入,
    -- 如果s2有``with ... check option``语句,那么这条程序就会失败
    insert into name_v_1 values (20,'Jack');
    

视图的更新

视图的更新:要使视图可更新,视图中的行与基础表(基表)中的行之间必须存在一对一的关系,如果视图包含以下任何一项,则该视图不可更新

比如:
在视图创建时里面包含一些
1.聚合函数(sum,min,max,count等),
2.DISTINCT–distinct关键字
3.GROUP BY – group by
4.HAVING – having
5.UNION或UNION ALL

视图的作用

1.操作简单
视图可以简化用户对数据的理解和操作,那些被经常使用查询的表就可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件

2.安全
数据库可以授权,但是最低也只能授权到表上,不能授权到数据库中特定行和特定列上,但是通过视图用户只能查询和修改他们所能见到的数据
比如,我只想让某一个用户看到并修改学生表的id和name字段,那么就可以使用视图来使这些用户只看到id和name

3.数据独立
视图可以帮助用户屏蔽真实表结构的变化带来的影响

视图的案例

MySQL进阶- SQL优化和视图_第10张图片

-- 1
create or replace view name_v_1 as select id,name,profession from 
tb_user_s1 with cascaded check option;

-- 2
create table student(
    id int primary key auto_increment comment 'id',
    name varchar(10) comment 'name',
    courseId int comment '课程id'
)comment '学生表';

create table course(
    id int primary key auto_increment comment 'id',
    courseName varchar(10) comment '课程名'
)comment '课程表';

create table stu_cou(
    id int primary key auto_increment comment 'id',
    stuId int comment 'stuId',
    souId int comment 'couId'
)comment '关系表';

alter table stu_cou add constraint fk_stu_id foreign key (stuId) references student(id);
alter table stu_cou add constraint fk_cou_id foreign key (souId) references course(id);

alter table student drop courseId;
insert into student values (1,'tom'),(2,'jack'),(3,'mike'),(4,'arthur');
select *from student;
insert into course values (1,'java'),(2,'js'),(3,'python'),(4,'c#');
insert into stu_cou values (1,1,1),(2,1,2),(3,1,4),(4,2,2),(5,2,3),(6,3,1),(7,3,2),(8,3,3),(9,4,1),(10,4,2),(11,4,3);

create or replace view vi_stu_cou as select distinct s.name,c.courseName from student s,stu_cou sc,course c where s.id=sc.stuId and c.id=sc.souId;

select *from vi_stu_cou;

你可能感兴趣的:(sql,mysql,数据库)