《MySQL高效编程》读书笔记一
http://www.lgygg.wang/lgyblog/2020/02/12/%e3%80%8amysql%e9%ab%98%e6%95%88%e7%bc%96%e7%a8%8b%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%e4%b8%80/
http://www.lgygg.wang/lgyblog/2020/02/12/%e3%80%8amysql%e9%ab%98%e6%95%88%e7%bc%96%e7%a8%8b%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%e4%ba%8c/
简介
《MySQL高效编程》一书主要是讲如何管理操作数据库的知识,并不是主要讲解如何数据库的书籍。
1.什么是数据库?
将数据收集到一起并不能称为数据库,只有能利用数据信息功能时才能被成为数据库库。
1)什么是数据库管理系统?
数据库只是用来存放数据的仓库,而对数据进行利用(增删改查擦操作)的系统,就叫做数据库管理系统。
2)什么是数据库应用程序?
通过数据库管理系统就可以对数据库执行所以的操作,例如要对数据进行增删改查,但是,对于没有数据库知识的人来说(普通人根本不懂如何通过SQL语句来进行查询数据等操作),不懂如何使用数据库管理系统,所以才会出现数据库应用程序。例如,如果一个用户需要查询关于“二十四节气”的意思,那么他可以直接在浏览器里打开google,百度等搜索引擎的主页,在输入框输入“二十四节气”,点击搜索,网页就会查询出关于“二十四节气”的相关内容。可以看到整个过程用户都没有使用到数据库专业知识(也就是没有使用SQL语句来查询数据)就可以获取相关信息。
2.数据库种类
阶层型/网络型数据库
卡片型数据库
关系型数据库
面向对象型数据库
XML型数据库
3.如何在命令行窗口执行MySQL操作?
这里只说Window环境下,安装好MySQL之后,找到MySQL的安装路径,我的安装路径为C:\Program Files\MySQL ,然后复制里面的路径C:\Program Files\MySQL\MySQL Server 8.0\bin,将这个路径添加到环境变量中,右击我的电脑,选择属性-》高级系统设置-》高级-》环境变量-》系统变量-》编辑path如下,
然后就可以打开命令行窗口,如下图,输入命令“mysql -u 用户名 -p 密码”,但是具体操作是需要你先输入“mysql -u 用户名 -p”,再输入密码。如果显示的内容如下图,就证明你已经启动了MySQL的监视器,你可以执行sql语句。
如下是测试执行sql语句显示数据库中所以的database
然后要关闭对MySQL的操作,就可以通过exit或者quit来退出
4.创建操作数据库的账户
用户可以使用命令:create database 数据库名
来创建数据库,数据库创建好之后,可以通过管理者(root)登录数据库进行各种操作。但从安全的角度来说,使用管理者(root)权限对所有的数据库进行操作并不太合适。出于安全着想,需要创建操作数据库的专用用户。可以使用语法:
Grant all privileges on 数据库.* to 用户名@localhost identified by 密码
如下例子:
Grant all privileges on home.* to user@localhost identified by ‘123456’;
这个例子创建专门操作名为home的数据库的用户user,并将密码设置为“123456”。这样赋予了从本地连接数据库的用户user能对home数据库中的所用对象进行操作的全部权限(all privileges)。权限是衡量用户能对数据库进行什么样的操作,具体的有create(创建),select(查询),update(更新),delete(删除)等权限。
[home.*]的意思是可以对home数据库里所有的表等进行操作,如果[home.customer],就是只能对home数据库里的customer表进行操作。
5.MySQL主要数据类型
6.修改表的列结构
Alter table命令是用来修改表的列结构的。根据修改类型的种类有,modify,change,add,drop等几种语法。
修改列的定义:alter table … modify
追加列:alter ttable … add
修改列的名称和定义:alter table … change
删除列:alter table … drop
下面举例子演示。
1)改变列的数据类型
列的数据类型是可以随时修改的。例如,将定义为varchar类型的列变为可以容纳大量字符的text类型。当然必须是可以使用的类型,修改为不能使用的类型的时候是会报错的。
注意,修改的时候,有时候会出现原来的数据变成乱码的情况,或出现一部分数据丢失的情况。例如,原来能容纳100个文字的列,如果将其修改为varchar(50)后,第50个字符之后的内容将消失。因此,如果表中原来有数据时,一般最好在对表结构进行修改前备份一下表。
下面就开始演示改变表数据类型。语法:
Alter table 表名 modify 列名 数据类型;
通过命令:
Alter table visitor modify nam varchar(30);
就可以将visitor表中的nam列名的数据类型varchar(20)改为varchar(30)。
2)追加新列
语法:alter table 表名 add 列名 数据类型;
下面例子展示在visitor表里追加新列年龄(old),
alter table visitor add old int;
还可以指定列的添加位置。
(1)在表的开头处追加新列
使用first关键字。如下,在visitor表的开头新增old列:
alter table visitor add old int first;
(2)在任意位置追加新列
使用after关键字将列追加到任意位置。例如将年龄(old)追加到姓名(nam)列之后:
Alter table visitor add old int after nam;
3)改变列的位置
使用关键字modify来修改已经定义列的位置,如下:
如何将visitor表中最好一个列年龄(old)移动到姓名后面,命令如下:
Alter table visitor add old int after nam;
4)改变列名
语法:alter table 表名 change 修改前的列名 修改后的列名 修改后的类型;
对于表中的列生日(birth),现在定义的为datetime类型,即可以保存到时刻[00:00:00]为止的时间,但是一般生日只需要保存年月日即可。所以需要将它改为date类型。通过下面的命令:
Alter table visitor change birth birthday date;
这样,就将原本的birth列改名为birthday并且将它的数据类型改为date,原本保存的[00:00:00]部分的数据将被删除。
5)删除列
语法:alter table 表名 drop 列名;
下面演示删除列年龄(old)
Alter table visitor drop old;
执行上面的语句,即可删除old列。
7.复制表
维护数据库的时候,会碰到需要输入大量数据到数据库的情况,这种情况如果人工输入的话就十分耗时,效率很低,下面介绍3种情况的复制方法。
1)表的列构造+数据的复制
实际上时“从检索出来的结果中复制构造列和记录,并创建新表”,是一种连带数据一起复制表的方法。
语法:create table 新表名 select * from 旧表名;
下图是《MySQL高效编程》书中的例子。
如果后面的检索条件(select)加上对应的检索条件(where)语句,或者限制记录的数量(使用limit语句),就只会复制满足条件的记录。
还需要注意的是,使用这种方法复制可能会发生列属性被改变的情况。例如,根据MySQL的版本,varchar(20)可能会被改为char(20)。另外,可能会发生不能复制index的有关设定的情况。所以,在复制完成后,一定要使用desc命令来确认表的构造是否正确。如下图,可以显示表student的构造。
2)表的列构造复制
此种方式只为复制表的列结构来创建新表。此种方法不会复制记录,[auto_incrment],[primary key]等列构造将被复制。
语法:create table 新表名 like 旧表名;
下图是《MySQL高效编程》书中的例子。
3)数据的复制
向已经创建好的表中复制数据。
语法:insert inito 表名 select * from 含有数据的表;
下图是《MySQL高效编程》书中的例子。
8.设置存储引擎(《MySQL高效编程》第六章)
MySQL有功能可以分为两部分,外层部分主要完成与客户端的连接及执行SQL语句的功能。而内层部分就是存储引擎。它负责接收外层的数据操作指示,完成实际的数据输入输出以及文件操作工作。其工作模式如下:
MySQL提供的引擎有:
1)查看当前表使用的引擎
语法:show create table 表名;
下图是《MySQL高效编程》书中的例子。
2)设置引擎
如上图,在创建表的时候,可以使用[engine=引擎]来设置想要的引擎。
3)变更引擎
语法:alter table 表名 engine=新引擎;
下图是《MySQL高效编程》书中的例子。
9.索引的使用
索引的出现是为了提升数据库的检索性能。
没有索引的表是如何检索的?下图是《MySQL高效编程》书中的例子。
下面是展示员工信息表(employee)执行以下select命令的例子。
在默认情况下,表中的记录是没有顺序的。我们并不知道符合条件的数据保存在表中的什么位置,这时候,数据库首先会从第一条记录开始检索。例如检索进行的过程中,找到了一个叫[wang]的人,并不意味着找到了所有叫[wang]的人,因为在数据库中所[wang]的人可能只有一个,也可能有很多个。因此,要将表中所有记录都遍历一遍,也就是所谓的全表扫描(或全件检索)。全表扫描的效率是很低的。
那么如何避免全表扫描这样低效率的搜索呢?使用索引就可以解决。
以上面的员工信息表(employee)来说明,如果我们事先为lname_pinyin这个列创建索引,那么这个索引就会将lname_pingyin(姓氏拼音)的数据与这条记录的位置信息保存在一个集合里,如下图,这样就不需要进行全表扫描,只需要从事先排列好的索引中只抽取符合条件的记录即可。
1)B数索引
大多数的数据库中用一种称为B树(Balanced Tree,平衡树)的结构来保存索引。除了B数索引外,还有其他结构来保存索引,如B+树索引,哈希索引等,注意,这里保存的是索引,而不是记录。也就是说以什么形式来保存索引。
B树结构就是像枝叶扩散开来的树状结构,各个节点中保存着复合关键字以及指针组成的数组,指针是用来确定数据的位置的,节点就是由这些指针相互关联起来的。如下图
B树一个显著的特点就是从根节点到各个叶子节点的距离都是相等的。除此之外,我们还要明白什么叫做聚簇索引和非聚簇索引。
聚簇索引是对磁盘上实际数据重新组织以按指定的一个或多个列的值排序的算法。特点是存储数据的顺序和索引顺序一致。一般情况下主键会默认创建聚簇索引,且一张表只允许存在一个聚簇索引。
在《数据库原理》一书中是这么解释聚簇索引和非聚簇索引的区别的:聚簇索引的叶子节点就是数据节点,而非聚簇索引的叶子节点仍然是索引节点,只不过有指向对应数据块的指针。
所以,《MySQL高效编程》书中下图例子展示的是非聚簇索引的查询过程,其实也很容易辨别出来依据lname_pinyin列创建出来的不是聚簇索引,因为这个lname_pinyin是可能重复的,而定义中也可以看出,一张表只有一个聚簇索引,且一般是主键,很明显lname_pinyin不可能是主键。
例子中,数据库首先将检索条件[fang]与根节点的各个值进行比较,发现没有符合条件的,根据某个条件(假设这里是根据lname_pinyin列里边的开头字母,按26个英文字母顺利来进行节点分支的),找到[fang]在[ai]和[peng]之间,下一步就开始检索以[ai]开头的分支节点,将节点的值与[fang]进行比较,但是也没有找到[fang],但是缩小了范围,确定了[fang]在[cao]和[he]开头的节点之间,随后去查找以[cao]开头的节点,在第二为位置找到了[fang],取出对应指针,就能找到对应的记录。
2)索引的创建
语法:create [unique] index 索引名 on 表名(列名,…);
3)显示表的索引信息
语法:show index from 表名;
下图来自《MySQL高效编程》一书的例子。
从上图中显示的索引信息代表的意思如下图:
4)丛生索引
在“显示表的索引信息”的例子中,明明只创建了一个索引,为什么会出现两个索引信息。这是因为在创建员工信息表(employee)的时候,伴随主键的定义而创建的特殊索引,称之为丛生索引。通常的索引只会在叶子节点保存指向实际表的指针,而丛生索引的叶子节点保存的是实际数据。下图也是来自《MySQL高效编程》
5)索引的分析
使用explain命令来分析索引的检索效率。不是说索引创建了就一定会使查询速度变快,不同的索引会造成不同的查询速度,因此我们需要分析索引的检索效率,选择合适的索引。
语法:explain 调查对象 select 语句;
这是《MySQL高效编程》的例子,首先,将之前给员工信息表(employee)创建的索引删除,查看检索lname_pingyin列的时候索引的使用情况。
下图展示的使explain命令输出各个参数表示的含义
然后,下图使对lname_pingyin创建索引后,查看它的检索情况如下:
将这个结果与没有对lname_pingyin创建索引之前的检索结果进行对比,发现对lname_pingyin创建索引后进行检索,发现rows从原来的9变成了2,也就是说遍历次数从原来的9遍变成了2遍,这就大大的提高了检索效率。
相反,如果遍历的次数和创建索引前变化不大,则说明创建索引时选择的列名不合理,需要重新选择合适的列作为索引。
6)索引起效果的场合
在某些情况下,即使你创建了索引,这些索引也不一定会起作用。下面举几个场景来提醒我们规范的使用索引。
(1)避免进行后方一致/部分一致检索
当使用like进行模糊检索的时候,只有在进行前方一致的检索才能使用上索引。而后方一致/部分一致检索的场合下是使用不上索引的。例如下面两个查询语句,是不会调用索引的
Select * from employee where lname_pinyin like ‘%w%’;
Select * from employee where lname_pinyin like ‘%w’;
(2)避免使用 is not null和<>比较运算符
下面两个语句也不会调用索引
Select * from employee where lname_pinyin is not null;
Select * from employee where lname_pinyin <> ‘wang’;
(3)避免对列使用运算/函数
对索引列使用了函数或者进行了某些运算的情况,也是不能使用索引的。如下例子是无法使用索引的
Select * from employee where year(birth) =’1980’;
可以想办法将上面的条件中的函数或一种方式,就能使用索引了,如下:
Select * from employee where birth >=’1980-01-01’ and birth <=’1980-12-31’;
(4)避免复合索引的第一列没有包含在where条件语句中
假设针对员工信息表(employee)创建的复合索引如下:
Create index idx_pingyin on employee(lname_pinyin,fname_pinyin);
针对这个索引,如果单独检索lname_pinyin列,或者同时检索lname_pinyin和fname_pinyin列时,该索引是会被调用的,如下:
Select * from employee where lname_pinyin =‘wang’ and fname_pinyin =‘xiao’;
Select * from employee where lname_pinyin =‘wang’;
而下面的检索语句是无法使用该复合索引的
Select * from employee where lname_pinyin =‘wang’ or fname_pinyin =‘xiao’;
Select * from employee where lname_pinyin =‘xiao;