数据库设计

数据库

  • 什么是sql
    • 一般结构化查询语言
  • 常见数据库
    • SQL Server
    • MySQL
    • Oracle
    • Sybase
    • DB2
    • H2
  • 为什么要用数据库
    • 存放数据方便,提高数据存放的效率及安全性,进行数据共享
  • 什么是数据库
    • 存放数据的仓库,将数据按照表的形式组织起来进行存放
  • 数据库的基础概念
    • 实体
      • 数据库中存放的每一条记录可以理解为一个实体,如学生表中的每一个学生,商品表中的每一个商品。
    • 属性
      • 每一个实体的特征,我们用属性来描述实体。(列、字段)
    • 数据
      • 如属性的属性值 001、张三就是数据。数据的形式保含文本信息、图片、视频、音频、日期时间等等
    • 常用的客户端
      • navicat sqlyog

数据库结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WENN08Sk-1645522464477)(image/202109181204910.gif)]

如果要使用CMD窗口作为客户端,需要配置环境变量,在path中添加如下变量值:Program Files\MySQL\MySQL Server 5.5\bin;

  • my.ini
  • 是mysql数据库核心配置文件

注意:使用数据库之前一定启动服务

  1. 数据库服务器
  2. mysql默认数据库
  • information_schema
    • 提供了访问数据库元数据的方式,数据库字典。 保存着关于MySQL服务器所维护的所有其他数据库的信息。(如数据库名,数据库的表,表栏的数据类型与访问权 限
  • mysql
    • mysql的核心数据库,类似于sql server中的master表,主要负责存储数据库的用户、权限设置、关键字等mysql自己需要使用的控制和管理信息
  • performance_schema
    • 数据库服务器的性能参数

Sql语句

MySQL的语法规范

  1. 不区分大小写,但建议关键字大写,表名、列名小写
  2. 每条命令最好用分号结尾
  3. 每条命令根据需要,可以进行缩进或换行
  4. 注释
  • 单行注释:#注释文字
  • 单行注释:-- 注释文字
  • 多行注释:/* 注释文字 */

SQl语句分类

DDL data definition language 数据定义语句 (create alter drop)

DML data manipulation language 数据操作语句 (insert update delete)

DQL data query language 数据查询语句(select)

DCL data control lanugage 数据控制语句(grant revoke commit rollback)

MySQL常用命令

MySQL服务的登录和退出

登录:mysql 【-h 主机名 -P 端口号】 -u 用户名 -p密码

退出:exit或ctrl+C

MySQL服务的启动和停止

方式一:通过dos窗口

net start 服务名

net stop 服务名

查看服务器的版本

方式一:登录到mysql服务端

select version();

方式二:没有登录到mysql服务端

mysql --version 或 mysql --V

查看SQL执行情况

explain

数据类型

整数类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IsgWayg2-1645522464478)(image/202109181205723.jpg)]

int(4):配合zerofill进行使用,显示占4位宽度,不够会补零,需要是无符号

小数类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rnrKQvBr-1645522464479)(image/202109181205465.jpg)]

DECIMAL(max(65),max(30)):最大数字位数和最大小数位数

如果精确运算,使用DECIMAL,没有精确运算的需求,建议使用float double

字符串类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iWQHAEWJ-1645522464479)(image/202109181205912.jpg)]

char和varchar的区别

  • 场景区别:char适合存放定长字符串,varchar存放长度可变的字符串
  • 后面的数字:字符个数,char后面最多可以写到255,varchar:一行数据最多占65535个字节(除去Bolb类的数据类型)
  • 除去其他列所占的空间,剩余的空间和varchar后面写的字符个数有关系,编码不同,一个字符所占用的存储空间也不一样,所以也会影响varchar后字符个数
  • char是固定长度,char(5),如果存放了3个字符,也会按照5个字符占用存储空间
  • varchar(5):可变长度,如果存储了3个字符,会按照3个字符占用存储空间。
  • 速度区别:char优于varchar
  • 空格的处理:char会消灭掉字符后自己插入的空格,varchar不会

二进制数据类型

日期和时间数据类型

date和datetime的区别

  • date类型可用于需要一个日期而不需要时间的部分
    • 格式为YYYY-MM-DD 范围是’1000-01-01’ 到’9999-12-31’
  • datetime类型可用于需要同时包含日期和时间的信息的值
    • 格式为YYYY-MM-DD HH:mm:ss 范围是’1000-01-0100:00:00’ 到 ‘9999-12-3123:59:59’

enum和set的区别:

enum只能从列出来的值中选择一个作为数据,set可以从列出来的值中选择多个值作为数据。

三大范式:

第一范式

  • 每列保证原子性,且唯一

第二范式

  • 首先满足第一范式
  • 必须要有一个主键
  • 如果主键是复合主键,除了主键以外的其他列必须完全依赖于主键列,不能只依赖于主键的一部分

第三范式

  • 满足第二范式
  • 除了主键以外的其他列必须直接依赖于主键,不能间接依赖于主键

MySQL安装

1、下载mysql5.5

http://dev.mysql.com/downloads/mysql/5.5.html#downloads

2、开始安装

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ehvau1mX-1645522464482)(image/202109181205560.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ADbl435m-1645522464482)(image/202109181205633.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUEYS99e-1645522464482)(image/202109181205104.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c6D76rDw-1645522464483)(image/202109181205937.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oqLDeK2o-1645522464483)(image/202109181205992.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aZii0oXI-1645522464483)(image/202109181206506.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQoJ74JE-1645522464484)(image/202109181206196.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cvu7dV3s-1645522464484)(image/202109181206608.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qyY9guTW-1645522464484)(image/202109181206449.png)]

3、进行配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IOJmuVwl-1645522464485)(image/202109181206025.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zKtTLKdH-1645522464485)(image/202109181206480.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kZNT4tRp-1645522464486)(image/202109181206347.png)]

此处选择详细配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPjUMPNZ-1645522464486)(image/202109181206050.png)]

此处选择开发机

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4rVSyjOh-1645522464486)(image/202109181206516.png)]

此处选择多功能数据库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pHcvUP8S-1645522464487)(image/202109181206992.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZffQgZ4Q-1645522464487)(image/202109181206306.png)]

直接下一步

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XirkidhX-1645522464488)(image/202109181206984.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xGw69hIv-1645522464488)(image/202109181206961.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RpXGGsN6-1645522464489)(image/202109181207594.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BI7OkJ37-1645522464489)(image/202109181207714.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q8YCdZJ9-1645522464490)(image/202109181207751.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ug1GhCw5-1645522464490)(image/202109181207654.png)]

库管理

建库操作

#创建数据库(默认字符集编码) 
create database test20210420 
#创建数据库的时候指定字符集编码以及字符校验规则 
create database test20210420 CHARACTER set = utf8 COLLATE utf8_general_ci 
#切换可用数据库(建表之前一定要切换) 
use test20210420 
#查看服务器的所有数据库 
show databases 
#删除数据库
drop database test20210420
#修改数据库字符集编码以及字符校验规则 
alter database test20210420 CHARACTER set = utf8 COLLATE utf8_general_ci 
#查看数据库信息 
show create database test20210420

表管理

建表操作

#创建表:创建表的格式 
create table student( student_id int, student_name varchar(20), student_birth int ) 
#插入数据的命令 
insert into student values(1,'姚明',20) 
#查询 
select * from student 
#删除表 
drop table student

复制表操作

#结构和数据一起复制(有创建表) 
create table testchar1 as select * from testchar 
#结构复制(有创建表) 
create table testchar2 like testchar

修改表操作

#1.给表中增加列 
alter table testchar add t_age int 
#2.给修改列名及列定义 
alter table testchar change t_name1 t_name2 varchar(50) 
#3.修改列定义
alter table testchar modify t_name2 varchar(100)
#4.删除列 
alter table testchar drop t_age

辅助命令

#查看当前数据库中所有的表 
SHOW TABLES; 
#查看表的定义信息 
SHOW CREATE TABLE testchar 
#删除表 
drop table testchar 
#表重新命名 
Rename table testchar to testchar3

约束

NOT NULL非空,该字段的值必填

UNIQUE唯一,该字段的值不可重复

DEFAULT默认,该字段的值不用手动插入有默认值

CHECK检查,mysql不支持

PRIMARY KEY主键,该字段的值不可重复并且非空 unique+not null

FOREIGN KEY外键,该字段的值引用了另外的表的字段

主键和唯一的异同:

区别:

①一个表至多有一个主键,但可以有多个唯一

②主键不允许为空,唯一可以为空

相同点

①都具有唯一性

②都支持组合键,但不推荐

主表和从表:

主表(父表)被引用字段所在的表

在数据库中建立的表格即Table,其中存在主键(primary key)用于与其它表相关联,并且作为在主表中的唯一性标识。

从表(子表)

以主表的主键(primary key)值为外键(Foreign Key)的表,可以通过外键与主表进行关联查询。从表与主表通过外键进行关联查询。

修改表时添加或删除约

#1、非空     
    #添加非空     
    alter table 表名 modify column 字段名 字段类型 not null;     
    #删除非空     
    alter table 表名 modify column 字段名 字段类型 ; 
#2、默认     
    #添加默认   
    alter table 表名 modify column 字段名 字段类型 default;     
    #删除默认     
    alter table 表名 modify column 字段名 字段类型 ; 
#3、主键     
    #添加主键     
    alter table 表名 addconstraint 约束名】 primary key(字段名);   
    #删除主键     
    alter table 表名 drop primary key; 
#4、唯一   
    #添加唯一   
    alter table 表名 addconstraint 约束名】 unique(字段名);     
    #删除唯一     
    alter table 表名 drop index 索引名; 
#5、外键   
    #添加外键   
    alter table 表名 addconstraint 约束名】 foreign key(字段名) references 主表(被引用列);   
    #删除外键   
    alter table 表名 drop foreign key 约束名;
#自增长列
    #添加自增长列
    alter tablemodify column 字段名 字段类型 约束 auto_increment
    #删除自增长列
    alter tablemodify column 字段名 字段类型 约束 

# 插入

方式一

语法:

insert into 表名 (字段名,...) values (,...);

特点:

1、要求值的类型和字段的类型要一致或兼容

2、字段的个数和顺序不一定与原始表中的字段个数和顺序一致

但必须保证值和字段一一对应

3、假如表中有可以为null的字段,注意可以通过以下两种方式插入null值

①字段和值都省略

②字段写上,值使用null

4、字段和值的个数必须一致

5、字段名可以省略,默认所有列

方式二

语法:

insert into 表名 set 字段=,字段=,...;

两种方式的区别:

1.方式一支持一次插入多行,语法如下:

insert into 表名(字段名,..) values(值,..),(值,...),...;

2.方式二支持子查询,语法如下:

insert into 表名
查询语句;

修改

一、修改单表的记录

语法:

update 表名 set 字段=,字段=where 筛选条件;`

二、修改多表的记录【补充】

语法:

update1 别名 
left|right|inner join2 别名 
on 连接条件  
set 字段=,字段=where 筛选条件;

删除

方式一:使用delete

一、删除单表的记录
语法:
delete from 表名 where 筛选条件 limit 条目数
二、级联删除[补充]
语法:

delete 别名1,别名2 from1 别名 
inner|left|right join2 别名 
on 连接条件
where 筛选条件

方式二:使用truncate

语法:

truncate table 表名

两种方式的区别【面试题】

  1. truncate删除后,如果再插入,标识列从1开始;delete删除后,如果再插入,标识列从断点开始
  2. delete可以添加筛选条件;truncate不可以添加筛选条件
  3. truncate效率较高
  4. truncate没有返回值;delete可以返回受影响的行数
  5. truncate不可以回滚;delete可以回滚

基础查询

语法:

select 列名1, ... ,列名n from 表名
where 条件        -- 1、条件
group by 列名     -- 2、分组
having 条件       -- 3、条件
order by 列名     -- 4、排序
limit 开始,条数   -- 5、分页

关于列的操作

1、查询所有列所有行

select * from 表名

2、查询指定列

#多列列名使用,隔开 
select 列名 from 表名

3、给列起别名

select 列名 [as] 别名,列名n [as] 别名n

4、列查询并进行算术运算

#列和固定数值
select 列名+\-\*\/\%数值 from 表名
#列和列
select 列名+\-\*\/\%列名 from 表名

5、多列进行合并为一列查询

select concat(列名1,列名2,...)合并后列名 from 表名

6、查询过程增加常量列

select * ,'常量' 列名 from 表名

条件查询(where)

1、比较运算符作为处理条件

(=,>,<,>=,<=,!=<>,如果是null,需要写为is\is not )

select * from 表名 where 列名=

2、多条件

1)同时满足(and)

select * from 表名 where 列名1=and 列名2=

2)或者(or)

#不同列
select * from 表名 where 列名1=or 列名2=#同一列的不同值
select * from 表名 where 列名 in (1,2,...)
select * from 表名 where 列名 not in (1,2,...)

3)模糊查询(like)

#查询l开头的名字,任意长度
select * from 表名 where 列名 like 'l%'
#查询l开头的名字,固定长度
select * from 表名 where 列名 like 'l__'
#查询包含o的名字
select * from 表名 where 列名 like '%o%'

4)上下界(between)

select * from 表名 where 列名 between 下界值 and 上界值

排序查询(order by)

升序

select * from 表名 order by 列名
select * from 表名 order by 列名 asc

降序

select * from 表名 order by 列名 desc

排序相等行,在进行排序

select * from 表名 order by 列名1 desc,列名2 desc

分页查询(limit)

#起始位置默认为0,查询不包括起始位置
select * from 表名 limit 起始位置,每页条目数

公式

#假如要显示的页数为page,每一页条目数为size
select 查询列表
fromlimit (page-1)*size,size;

聚合函数

关于某一列进行操作,和行没有关系

count() 返回结果集中行的数目
max() 返回结果集中所有值的最大值
min() 返回结果集中所有值的最小值
sum() 返回结果集中所有值的总和
avg() 返回结果集中所有值的平均值

用法

聚合函数null值不参与运算,如果希望null值也参与那么需要**ifnull()**函数处理

select 函数名(列名) from 表名

分组查询(group by)

select 分组列名,聚合函数 from 表名 group by 分组列名
#一般和聚合函数一起使用
#如,查询student表中的男女两个分组中的最大年龄和最小年龄
select ssex,max(sage),min(sage) from student group by ssex

条件筛选(having)

group by后不可以使用where进行筛选,需要使用having关键字

如果能用where筛选数据的话,绝不使用having

where和having的区别?

  • 都是进行条件判断的关键字
  • where条件判断是在分组前判断,having条件判断是在分组后判断
  • where关键字要写在group by前面,having关键字要写在group by后面
  • where条件里不能写聚合函数,having条件里可以写聚合函数
#如,查询年龄大于11的各个年龄段的人数
select sage,count(sage) from student group by sage having sage > 11
#可以使用where就不要用having
select sage,count(sage) from student where sage > 11 group by sage

联合查询(union)

  1. 要求多条查询语句的查询列数必须一致
  2. 要求多条查询语句的查询的各列类型、顺序最好一致
  3. union 去重,union all包含重复项
1.2个表的数据进行拼接,针对拼接后的重复数据 去重显示
select 列名 from 表名1 where 条件
union 
select 列名 from 表名2 where 条件
2.2个表的数据进行拼接,针对拼接后的重复数据 不去重显示
select 列名 from 表名1 where 条件
union all
select 列名 from 表名2 where 条件

多表联合查询

#sql92
select * from1,2;
#sql99
select * from1 cross join2;

笛卡尔积组合,形成数据没有价值

内连接(inner join)

select 列名 from A表 
inner join B表 on 关联条件;

外连接

左外连接(left)

select 列名 from A表 
left join B表 on 关联条件;

右外连接(right)

select 列名 from A表 
right join B表 on 关联条件;

自连接

#查询1号课程成大于2号课程的学生id
select * from sc s1  
inner join sc s2 
on s1.sno=s2.sno
where s1.cno=1 and s2.cno=2 and s1.score>s2.score

去重(distinct)

select distinct 列名 from 表名;

子查询

嵌套查询,就是指一个sql语句里面还有一条sql语句

将查询的结果(可以作为值,也可以作为表),再次被sql语句使用

结果为一个值

#查询李华老师所带课程
select * from course where tno = 
(
    	select tno from teacher where tname = '李华'
)

结果为一个表(多个值)

#查询李华老师所带学生的信息
-- 1 获取李华老师的编号
select tno from teacher where tname = '李华'
-- 2 根据老师的编号获取所带学科的编号
select cno from course where cno in 
(
    	select tno from teacher where tname = '李华'
)
-- 3 根据学科编号获取学生的编号
select sno from sc where cno in 
(
	select cno from course where cno in 
	(
			select tno from teacher where tname = '李华'
	)
)
-- 4 根据学生编号,获取学生的信息
select * from student where sno in 
(
	select sno from sc where cno in 
	(
		select cno from course where cno in 
		(
				select tno from teacher where tname = '李华'
		)
	)
)

any(满足该字段任何一个值,‘或’)

#查询学生表中,年龄大于张三或tony的学生信息
select * from student where sage > any(
    select sage from student where sname = '张三' or sname = 'tony'
)

all(满足该字段所有的值,‘且’)

#查询学生表中,年龄大于张三并且大于tony的学生信息
select * from student where sage > all(
    select sage from student where sname = '张三' or sname = 'tony'
)

exists和in的区别

扩展

按照条件显示不同信息(case、when、then、else、end)

#查询student表中,按照年龄显示是否成年
select *,
case 
	when sage >= 18 then '成年'
    else '未成年'
    end
from student

备份和恢复命令

备份库

直接在cmd窗口中直接输入,结束不需要输入;

mysqldump -h端口号 -u用户名 -p密码 数据库名>备份地址

恢复库

在cmd窗口中进行
1、连接数据库
mysql -u用户名 -p密码
2、创建数据库
create database 库名
3、切换到可用数据库
use 库名
4、进行恢复
source 备份文件地址

授权:

新用户信息增改

1.创建用户:
# 指定ip:192.118.1.1的用户登录
create user '用户名'@'192.118.1.1' identified by '密码';
# 指定ip:192.118.1.开头的用户登录
create user '用户名'@'192.118.1.%' identified by '密码';
# 指定任何ip的用户登录
create user '用户名'@'%' identified by '密码';
2.删除用户
drop user '用户名'@'IP地址';
3.修改用户
rename user '用户名'@'IP地址' to '新用户名'@'IP地址';
4.修改密码
set password for '用户名'@'IP地址'=Password('新密码');

用户权限管理

#查看用户权限
show grants for '用户名'@'IP地址'
1、授权
#授权用户仅对某文件有查询、插入和更新的操作
grant select,insert,update on 文件名 to '用户名'@'IP地址';
#授权所有的权限,除了grant这个命令,这个命令是root才有的。用户对db1下的t1文件有任意操作
grant all privileges  on db1.t1 to '用户名'@'IP地址';
#授权用户可以对db1数据库中的所有文件执行任何操作
grant all privileges  on db1.* to '用户名'@'IP地址';
#授权用户可以对所有数据库中文件有任何操作
grant all privileges  on *.*  to '用户名'@'IP地址';
 
2、取消权限
# 取消用户对db1的t1文件的任意操作
revoke all on db1.t1 from '用户名'@'IP地址';  
# 取消来自远程服务器的mjj用户对数据库db1的所有表的所有权限
revoke all on db1.* from '用户名'@'IP地址';  
# 取消来自远程服务器的mjj用户所有数据库的所有的表的权限
revoke all privileges on *.* from '用户名'@'IP地址';

为什么使用视图

多表的联合查询,最多也才3张表,如果面临更多的表,为了简化连表操作,可以使用MySQL中的视图

好处

1、简化sql语句

2、提高了sql的重用性

3、保护基表的数据,提高了安全性

创建视图

create view 视图名
as
查询语句;

修改视图

方式一

create or replace view 视图名
as
查询语句;

方式二

alter view 视图名
as
查询语句

删除视图

drop view 视图1,视图2,...;

查看视图

desc 视图名;
show create view 视图名;

视图和表的对比

关键字 是否占用物理空间 使用
视图 view 占用较小,只保存sql逻辑 一般用于查询
table 保存实际的数据 增删改查

什么是索引

索引用于快速找出在某个列中有一特定值的行,避免全表扫描

MySQL索引,默认是B+树索引

查询表中的索引

show index from 表名

索引的优缺点

优点:

1、所有的MySQL列类型(字段)都可以被索引,也就是可以给任意字段设置索引

2、大大加快数据的查询速度

缺点:

1、创建索引和维护索引要耗费时间,并且随着数据量的增加所耗费的时间也会增加

2、索引也需要占空间,如果有大量的索引,索引文件可能会比数据文件更快达到上限值

3、当对表中的数据进行增、删、改时,索引也需要动态的维护,降低了数据的维护速度

索引的分类

单列索引(普通索引、唯一索引、主键索引)、组合索引、全文索引、空间索引

创建索引

普通索引(b+树索引)

alter table table_name add index index_name(column_name) using btree;
create unique index index_name on table_name(column_name);

组合索引


唯一索引


全文索引


什么是事务

事务是作为单个逻辑单元执行的一系列操作

多个操作作为一个整体向系统提交,要么执行、要么都不执行,事务是一个不可分割的工作工作逻辑单元

四大特性(ACID)

原子性(A – Atomicity):原子是参与化学反应中最小的粒子,不能再分割了,也就是说事务就是最小的,不能再分割了,如果把事务都分割了,就会出现问题

一致性(C – Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏

隔离性(I – Isolation):事务与事务之间互不干扰

持久性(D – Durability):通过事务进行操作后的数据,是永久保存的

注意:在实际开发中,经常会打破隔离性,当多个事务共同操作同一张表的时候,一旦打破了隔离性,就会出现安全问题

存储引擎

存储引擎:RDBMS中,决定了数据如何存储,如何获取,如何控制事务,如何控制外键等一系列功能的一套程序

**常用引擎:**InnoDB,MyIsam

InnoDB与MyIsam的区别

  1. InnoDB支持事务,支持外键;而MyIsam不支持事务,不支持外键
  2. InnoDB由于受到事务和外键的影响,所以对数据的存储以及查询效率偏低;MyIsam相反偏高
  3. InnoDB在存储时,表文件是2个:frm,ibd;而MyIsam是3个文件,分别存储frm,MYD,MYI
  4. InnoDB是MYSQL 5.5之后的默认存储引擎;而MyIsam是5.5之前的默认存储引擎

事务操作

方式一

开启事务start transaction;

提交事务commit;

注意:当事务开启之后,只有执行了commit,数据才会真的改变,如果没有执行commit,数据还原

回滚事务rollback;

方式二

修改默认的提交方式,默认是自动提交,我们要改成手动提交

show variables like '%autocommit%'

set @@autocommit=0(默认为1,自动提交;0,手动提交)

隔离级别

隔离级别 读数据一致性及允许的并发副作用 备注
读未提交(Read uncommitted) 最低级别,只能保证不读取物理上损坏的数据,事务可以看到其他事务没有被提交的数据(脏数据)
读已提交(Read commited) 语句级,事务可以看到其他事务已经提交的数据 Oracle数据库默认
可重复读(Repeatable read) 事务级,事务中两次查询的结果相同 MySql数据库默认
可串行读(序列化Serializable) 最高级别,事务级。顺序执行

隔离等级越高,数据库事务并发执行能力越差,能处理的操作越少。因此在实际项目开发中为了考虑并发性能一般使用读已提交隔离级别,他能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁乐观锁来解决这些问题。

什么是幻读、不可重复度、脏读

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据,读取到未提交的数据。

2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。

3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读

隔离级别与更新丢失的情况

  • 第一类更新丢失
    • 事务A撤销时,把已经提交的事务B的更新数据覆盖了
  • 第二类更新丢失
    • 事务A覆盖事务B已经提交的数据,造成事务B所做的操作丢失
隔离级别 脏读 不可重复读 幻读 第一类丢失更新 第二类丢失更新
Read uncommitted ×
Read commited × ×
Repeatable read × × × ×
Serializable × × × × ×

设置隔离级别

set session transaction isolation level 隔离级别

查看隔离级别(当前客户端)

select @@tx_isolation

乐观锁

默认存在,什么都不做,就是乐观锁。总是乐观的认为,在维护这个数据的时候,没有其他人来维护。

insert update delete 在执行SQL的同时,才给数据加上了乐观锁。

可能会导致的问题

age:12
A用户:update table set age=32 where id=1
B用户:update table set age=12 where id=1
A用户:select * from table where id=1
这个时候,A用户查看的数据为B修改过的,自己的修改已经被覆盖

悲观锁

总是担心,在自己修改数据的时候,有其他人,将这个数据修改,所以在修改数据之前,提前锁住数据

默认没有开启,需要自己开启

开启命令

#在查询语句后加for update就可以开启(悲观排他锁)
select * from book for update

优缺点

优点:准确性高,更加安全

缺点:效率太低,一旦一个用户执行悲观锁,那么其他用户都无法查看这个表的数据,也无法进行修改,除非执行悲观锁的用户commit

行锁

只会锁住一行数据

select * from book where id=1 for update

表锁

会锁住整个的一个表

select * from book for update

排他锁

A用户在给表加排他锁以后,那么其他用户都无法对表进行操作、查看、加锁

//表级排他锁
lock table book write
//解锁
unlock table

共享锁

所有的用户都可以对表进行加锁,但是,所有的用户都无法对表进行操作,包括上锁的用户

//表级共享锁
lock table book read
//解锁
unlock table

死锁

两个线程自己各自持有自己数据的锁,但互相都想锁住被对方锁住的数据,就产生了死锁

系统检索到死锁,会自动释放一个锁

数据持久化

数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称。数据模型可以是任何数据结构或对象模型,存储模型可以是关系模型、XML、二进制流等。

瞬时状态

保存在内存的程序数据,程序退出,数据就消失了

持久状态

保存在磁盘上的程序数据,程序退出后依然存在

数据持久化技术

Hibernate、JPA、==JDBC(Java Datebase Connectivity)==等

JDBC框架

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a19x0KJX-1645522331453)(image/202109181208806.gif)]

Driver 接口

java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现

在程序中不需要直接去访问实现了 Driver 接口的类,而是由**驱动程序管理器类(java.sql.DriverManager)**去调用这些Driver实现

连接、操作数据库步骤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzX1KIF4-1645522331455)(image/202109181208475.gif)]

Connection conn = null; 
Statement st=null; 
ResultSet rs = null; 
try { 
//获得Connection 
//创建Statement 
//处理查询结果ResultSet 
}catch(Exception e){ 
    e.printStackTrance(); 
} finally {
    //释放资源ResultSet, Statement,Connection 
}

一、获取数据库连接对象步骤

1、导入jar包

1、在项目中创建lib文件夹

2、将jar文件放置到lib文件夹

3、集成到项目中,右键build(eclipse)、add as library(idea)

2、注册驱动(Java代码中加载驱动类)

将com.mysql.jdbc包下的Driver类的字节码文件从本地磁盘加载到方法区中

Oracle的驱动oracle.jdbc.driver.OracleDriver

mySql的驱动com.mysql.jdbc.Driver

方式一:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名

//将com.mysql.jdbc包下的Driver类的字节码文件从本地磁盘加载到方法区中
Class.forname("com.mysql.jdbc.Driver")

方式二:DriverManager 类是驱动程序管理器类,负责管理驱动程序

DriverManager.registerDriver(com.mysql.jdbc.Driver);

通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实例,原因:

1、该方法,过于依赖jar包的存在

2、该方法,会造成二次注册

3、使用Class.forname可以降低耦合性

3、获取连接对象

//本机IP:localhost 本机端口号:3306
String url = "jdbc:mysql://IP地址:端口号/库名?serverTimezone=Asia/Shanghai&characterEncoding=utf-8";
String user = "用户名";
String passWord = "密码";
Connection conn = DriverManager.getConnection(url,user,passWord);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ua1UJT6D-1645522331455)(image/202109181208986.gif)]

协议:JDBC URL中的协议总是jdbc

子协议:子协议用于标识一个数据库驱动程序

子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为 了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名

几种常用数据库的JDBC URL

对于 Oracle 数据库连接,采用如下形式:

jdbc:oracle:thin:@localhost:1521:库名

对于 SQLServer 数据库连接,采用如下形式:

jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=库名

对于 MYSQL 数据库连接,采用如下形式:

jdbc:mysql://localhost:3306/库名

二、执行sql语句

1、获取Statement对象

Statement statement = conn.createStatement();

2、执行sql语句

int result = statement.executeUpdate("sql语句字符串对象")
Statement类方法分类
  • int executeUpdate(sql);
    • 针对数据库的增(insert into)、删(delete from)、改(update set)操作
    • 返回值类型:实际影响的行数
  • ResultSet executeQuery(sql);
    • 针对数据库的查询(select from)操作
    • 返回值类型:一个结果集类型
  • boolean execute(sql);
    • 针对数据库的增删改查操作,一般我们不会使用,jdbc的底层代码会使用
    • 如果执行的sql语句是增删改,返回false
    • 如果执行的sql语句是查询,返回true

3、处理执行结果(ResultSet)

//使用Statement类的方法ResultSet executeQuery(String sql);获得结果集类型的对象
ResultSet set = statement.executeQuery(sql);
while(set.next()){
    //形参可以直接写字段名,字段名不区分大小写
    String id = set.getInt("book_id");
    //也可以写字段索引,索引从1开始
    String id = set.getInt(1);    
}

4、释放资源

resultSet.close();
statement.close();
connection.close();

实现JDBC工具类

将获取连接和关闭资源等公共、重复的代码封装成一个工具类

import java.sql.*;
public class JDBCUtil {
    private static String driver;
    private static String url;
    private static String user;
    private static String passWord;
    //解析配置文件.properties
    static {
        try {
             Properties properties = new Properties();
            properties.load(new FileInputStream(".properties文件路径"));
            driver = (String) properties.get("driver");
            url = (String) properties.get("url");
            user = (String) properties.get("user");
            passWord = (String) properties.get("passWord");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //获得Connection对象
    public static Connection getConnection(){
        Connection connection = null;
        try{
            Class.forName(driver);
            connection = DriverManager.getConnection(url,user,passWord);
        }catch (Exception e){
            e.printStackTrace();
        }
        return connection;
    }
    //关闭资源 -- 针对查询
    public static void close(ResultSet resultset,Statement statement,Connection connection){
        try {
            if (resultset != null) {
                resultset.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //关闭资源 -- 针对增删改
    public static void close(Statement statement,Connection connection){
        close(null,statement,connection);
    }
    //针对DML语句--增删改
    public static boolean executeUpdate(String sql,List<Object> list){
        Connection connection = getConnection();
        PreparedStatement pre = null;
        try {
            pre = connection.prepareStatement(sql);
            for (int i = 0;i < list.size();i++){
                pre.setObject(i + 1,list.get(i));
            }
            return (pre.executeUpdate() > 0)? true : false;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            close(pre,connection);
        }
        return false;
    }
    //针对查DQL语句
    public static <T> List<T> executeQuery(String sql,List<Object> list,Class<T> tClass){
        Connection connection = getConnection();
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        List<T> li = new ArrayList<>();
        try {
            statement = connection.prepareStatement(sql);
            for (int i = 0;i < list.size();i++){
                statement.setObject(i + 1,list.get(i));
            }
            resultSet = statement.executeQuery();
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            //获取列数
            int count = resultSetMetaData.getColumnCount();
            //遍历所有行
            while (resultSet.next()){
                T t = tClass.newInstance();
                for (int i = 1;i <= count;i++){
                    //获取每一列列名
                    String keyName = resultSetMetaData.getColumnLabel(i);
                    //获取每一列对应的值
                    Object value = resultSet.getObject(keyName);
                    //T中对应的属性
                    Field key = tClass.getDeclaredField(keyName);
                    key.setAccessible(true);
                    key.set(t,value);
                }
                li.add(t);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            close(connection,statement,resultSet);
        }
        return li;
    }
}

封装查询返回值遍历方式

List<Map> list = JDBCUtils.executeQuery(sql,new ArrayList());
for (Map<String,Object> map : list){
    for (Map.Entry<String,Object> entry : map.entrySet()){
        String s = entry.getKey();
        Object o = entry.getValue();
        System.out.print(s + "=" + o + ",");
    }
    System.out.println();
}

sql注入攻击

SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在 用户输入数据中注入非法的 SQL 语句段或命令,如下,从而利用系统的 SQL 引擎完成恶意行为的做法。

SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1'

对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(继承于Statement) 取代 Statement 就可以了

PreparedStatement类

1、可以通过调用 Connection 对象的 preparedStatement() 方法获取 PreparedStatement 对象

2、PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句

PreparedStatement类和Statement的比较

1、代码的可读性和可维护性

2、PreparedStatement 能最大可能提高性能

3、PreparedStatement 可以防止 SQL 注入

4、如果拼接表名、列名、关键字,必须使用Statement,防止sql语句错误

ResultSet类

1、通过调用 PreparedStatement 对象的 excuteQuery() 方法创建该对象

2、代表结果集

3、ResultSet 返回的实际上就是一张数据表.,有一个指针指向数据表的第一条记录的前面。

ResultSetMetaData 类

1、通过调用ResultSet对象的getMetaData()方法创建改对象

2、可用于获取关于 ResultSet 对象中列的类型和属性信息的对象

常用方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yytazs2B-1645522331456)(image/202109181209018.gif)]

JDBC封装Dao

**DAO (Data Access objects 数据存取对象)**是指位于业务逻辑和持久化数据之间实现对持久化数据的访问。通俗来讲,就是将数据库操作都封装起来。能够是代码的结构更加清晰化。

DAO 模式组成

  1. DAO接口: 把对数据库的所有操作定义成抽象方法,可以提供多种实现。
  2. DAO 实现类: 针对不同数据库给出DAO接口定义方法的具体实现。
  3. 实体类:用于存放与传输对象数据。
  4. 数据库连接和关闭工具类: 避免了数据库连接和关闭代码的重复使用,方便修改

Mysql的逻辑架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tDUYButJ-1645522338213)(image/image-20211031174725105.png)]

四层逻辑架构

  • 连接层
    • 主要完成一些类似连接处理,授权认证及相关的安全方案
    • 连接管理与安全性
      • 每个客户端连接都会在服务器进程中拥有一个线程,每一个连接的查询只会在这个单独的线程中执行,该线程轮流在某个CPU核心或者CPU中运行。服务器负责缓存线程。不需要为每一个新建的连接创建或者销毁线程。MySQL5.5版本之后,支持线程池插件,可以使用线程池当中少量的线程来服务大量的连接
      • 当客户端(应用)连接到MySQL服务器时,服务器需要对其进行认证。认证基于用户名、原始主机信息和密码。一旦客户端连接成功,服务器会继续验证该客户端是否具有执行某个特定查询的权限(是否允许对某个表执行SELECT语句)
  • 服务层
    • 在 MySQL据库系统处理底层数据之前的所有工作都是在这一层完成的,包括权限判断,SQL接口,SQL解析,SQL分析优化, 缓存查询的处理以及部分内置函数执行(如日期,时间,数学运算,加密)等等。各个存储引擎提供的功能都集中在这一层,如存储过程,触发器,视图等
    • SQL Interface-SQL接口
      • 接受用户的SQL命令,并且返回用户需要查询的结果。比如select from就是调用SQL Interface
    • Parser-解析器
      • SQL命令传递到解析器的时候会被解析器验证和解析。解析器是由Lex和YACC实现的,是一个很长的脚本
    • Optimizer-查询优化器
      • SQL语句在查询之前会使用查询优化器对查询进行优化。就是优化客户端请求的 query(sql语句),根据客户端请求的 query 语句,和数据库中的一些统计信息,在一系列算法的基础上进行分析,得出一个最优的策略,告诉后面的程序如何取得这个 query 语句的结果
    • Cache和Buffer-查询缓存
      • 他的主要功能是将客户端提交 给MySQL 的 Select 类 query 请求的返回结果集 cache 到内存中,与该 query 的一个 hash 值做一个对应。该 Query 所取数据的基表发生任何数据的变化之后, MySQL 会自动使该 query 的Cache 失效。在读写比例非常高的应用系统中, Query Cache 对性能的提高是非常显著的。当然它对内存的消耗也是非常大的
  • 引擎层
    • 是底层数据存取操作实现部分,由多种存储引擎共同组成。真正负责MySQL中数据的存储和提取。就像Linux众多的文件系统 一样。每个存储引擎都有自己的优点和缺陷。服务器是通过存储引擎API来与它们交互的。这个接口隐藏 了各个存储引擎不同的地方。对于查询层尽可能的透明。这个API包含了很多底层的操作。如开始一个事物,或者取出有特定主键的行。存储引擎不能解析SQL,互相之间也不能通信。仅仅是简单的响应服务器 的请求
  • 存储层
    • 将数据存储于裸设备的文件系统之上,完成与存储引擎的交互

MySQL存储引擎

查看MySQL中现在提供的存储引擎:

show engines;

查看MySQL现在默认使用的存储引擎:

show variables like '%storage_engine%';

MyISAM引擎和InnoDB引擎简单对比:

MyISAM引擎 InnoDB引擎
外键 不支持 支持
事务 不支持 支持
行表锁 表锁.不适合高并发 行锁.适合高并发
缓存 只缓存索引 缓存索引和真实数据
表空间
关注点 性能.偏读 事务
默认安装

MySQL日志

MySQL的日志分类

  • 错误日志
    • -log-err (记录启动,运行,停止mysql时出现的信息)
  • 二进制日志
    • -log-bin (记录所有更改数据的语句,还用于复制,恢复数据库用,不建议开启)
  • 查询日志
    • -log (记录建立的客户端连接和执行的语句)
  • 慢查询日志
    • -log-slow-queries (记录所有执行超过long_query_time秒的所有查询)
  • 更新日志
    • -log-update (二进制日志已经代替了老的更新日志,更新日志在MySQL 5.1中不再使用)

查询当前日志记录的状况

show variables like 'log%';

错误日志

默认情况下,日志存放在C:\ProgramData\MySQL\MySQL Server 5.5\data下,文件名是:当前主机名.err,默认错误日志打开,无法关闭

慢查询日志

默认没有开启

临时开启:重启后失效

SET GLOBAL slow_query_log=1; #开启了慢查询日志
SET GLOBAL long_query_time=3;
SHOW VARIABLES LIKE '%slow_query_log%';

永久开启

修改MySQL安装目录中my.ini文件

# General and Slow logging.
log-output=FILE
general-log=1
general_log_file="general.log"
slow-query-log=1
slow_query_log_file="slow.log"
long_query_time=3

测试

SELECT SLEEP(3);

执行后,可以看见慢查询日志

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcTmEZ84-1645522338214)(image/202109181209385.png)]

Explain

可以模拟优化器执行SQL语句,从而知道Mysql是如何处理你的SQL语句的,分析你的SQL语句或者表结构的性能瓶颈

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rs7vKQbJ-1645522338215)(image/202109181209681.png)]

id

验证表的读取和加载顺序,select查询的序列号,包含一组数字,表示查询中,执行select子句或操作表的顺序

  • 不同id的执行顺序
    • id相同
      • 执行顺序从上向下
    • id不同
      • 如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
    • id有相同也有不同
      • 序号大的先执行,序号相同的,顺序执行

select_type

查询的类型,主要是用于区别普通查询,联合查询,子查询等的复杂查询

select type 解释 测试sql
simple 简单的select select * from tbl_student
简单的查询,查询中不包含子查询或者UNION。
primary 需要union或者子查询 EXPLAIN #年龄大于平均年龄的同学
SELECT * FROM tblstudent WHERE stuage>(
SELECT AVG(stuage) FROM tblstudent t1
)
查询中若包含任何复杂的子部分,最外层则被标记为PRIMARY,一般Primary也是最后被加载的那个
subquery 在select或者Where包含的子查询 上面的SQL就有subquery
derived 派生表 在From列表中,包含的子查询被标记为derived,MYSQL会递归执行这些子查询,把结果放在哪临时表(变量内存交换)里面。
select * from (select * from tb_student) t
#省市区 注意t1是一个虚表
EXPLAIN
select t2.* from
(SELECT cid FROM zone WHERE zname=‘未央区’) t1,city t2
where t1.cid=t2.cid
union union 如第二个select出现在union之后,则被标记为UNION
若union包含在From子句的子查询中 ,外层select将被标记为Derived select * from tb_student union select * from tb_student
union result union结果集 两种结果的合并,临时表
select * from tb_student union select * from tb_student
depend subquery 类似depend union select (select name from test.tb_student a where a.id=b.id) from test.tb_student b
dependent union 查询与外部相关 (mysql优化器会将in优化成exists) select * from tb_student where id in(select id from tb_student union select id from tb_student)
select * from tb_student a where EXISTS (select 1 from tb_student where id = a.id union select id from tb_student where id = a.id)

type

  • System
    • 表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,可以忽略不计
  • const
    • 表示通过索引一次就找到了,const用于比较primary key或者unique索引,因为只匹配一行数据,所以很快,如将主键置于where列表中,Mysql就能将该查询转换为一个常量
  • eq_ref
    • 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或者唯一索引
  • ref
    • 非唯一性索引扫描,返回匹配某个单独值的所有行,简单说,用索引查出了多条记录
  • range
    • 只检索给定范围的行,使用一个索引来选择行,key列显示是用来那个索引,一般就是在where句中出现了between, < , > , in等的查询
  • index
    • Full Index Scan全索引扫描 ,index只遍历索引树,通常比All快,有些情况,索引会被加载到内存中。读索引肯定比权标扫描要快
  • All
    • Full Table Scan 全表扫描
      查询效率的高低
      system>const>eq_ref>ref>range>index>ALL
      一般来说,保证查询至少达到range级别,最好达到ref级别

possible_keys&key

possible_keys

显示可能应用在这张表中的索引,一个或多个

查询设计到的字段上若存在索引,则该索引将被列出。但不一定被查询实际使用。

key

实际使用到的索引,如果为null,则没有使用索引

查询中若是用了覆盖索引,则该索引仅出现在key列表中

key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好

Key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的

re

显示索引的那一列被使用了,如果可能的话,是一个常数。那些列或常量被用于查找索引列上的值

rows

根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数。

每张表有多少行被优化器查询

extra

包含不适合在其他列中显示,但是十分重要的信息

  • Using fileSort文件内排序
    • 在使用order by关键字的时候,如果待排序的内容不能由所使用的索引直接完成排序的话,那么mysql有可能就要进行文件排序
  • using temporary内部临时表
    • 需要把数据先拷贝到临时表,最后再删除。也非常的慢,最好不要发生。如果要发生,要确保数据量
    • 使用临时表保存中间结果,Mysql在对查询结果排序时使用临时表。常见于排序order by和分组查询group by,往往见于统计分析
  • using index
    • select操作中使用了覆盖索引,Covering Index,避免访问了表的数据行,效率高
    • 如果同时出现using where ,表明索引被用来执行索引键值的查找
  • using where
    • 使用了条件查询
  • using join buffer
    • 使用了链接缓存
  • impossible where
    • where子句的值总是false,不能用来获取任何元素
  • Using index condition
    • 查询的列不全在索引中,where条件中是一个前导列的范围

索引优化

单表

例如,有商品表goods

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A29ZX1Tz-1645522338216)(image/202109191754701.png)]

执行sql语句,查询所有状态为1且价格大于20的商品,并对库存进行排序

EXPLAIN
SELECT * FROM goods WHERE gstate=1 AND gprice>20 ORDER BY gcount

发现结果为,此时不仅类型为全表扫描,而且进行了文件内排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BiSCOD7B-1645522338216)(image/202109191755294.png)]

所以,建立索引,因为查询使用到的字段为gpricegcountgstate,所以添加这三个字段的联合索引

CREATE INDEX goods_idx_all ON goods(gstate,gprice,gcount)

再次执行sql语句

EXPLAIN
SELECT * FROM goods WHERE gstate=1 AND gprice>20 ORDER BY gcount

发现此时,虽然命中索引了,而且type也为range,但是还是进行了文件内排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2sqLj5ph-1645522338217)(image/202109191800073.png)]

如果将sql语句中的范围查询改为固定常量

EXPLAIN
SELECT * FROM goods WHERE gstate=1 AND gprice=20 ORDER BY gcount
``
发现结果正常,没有文件内排序了,所以是范围查询引起的文件内排序,原因是:Btree的工作原理,在创建索引的时候,会先对gstate进行排序,如果gstate相同,对gprice进行排序,如果gprice相同,对gcount进行排序,而gprice处于联合索引中间的位置,如果进行范围查询,也就是range,那么范围查询字段后面的索引将失效

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9iWYixbC-1645522338217)(image/202109191802429.png)]
解决方法是

**需要范围查询的字段,不参与联合索引的创建**
## 多表
```sql
EXPLAIN
SELECT * FROM orders LEFT JOIN goods ON orders.git=goods.git

执行结果,orders表,进行了全表扫描,而goods表,因为有主键索引,索引命中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qXZhWEZb-1645522338218)(image/202109191957352.png)]

索引的建立规则
如果使用left join,那么关联字段的索引建立在右表上,right join,索引建立在左表上

索引失效的解决

1、最好全值匹配,索引怎么建立的,就怎么用,使用and连接的查询语句,可以不与索引顺序相同,因为sql优化器会自动进行优化

2、最佳左前缀法,查询时,要从索引建立的最左前列(第一列)开始,不可以跳过中间的列,例如

建立索引

CREATE INDEX goods_idx_all ON goods(gcount,gstate,gprice)

只查询前两列,gcount、gstate,结果正常

EXPLAIN
SELECT * FROM goods WHERE gcount=20 AND gstate=1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p5hSzzh5-1645522338218)(image/202109192008994.png)]

只查询第一列,gcount,结果正常

EXPLAIN
SELECT * FROM goods WHERE gcount=20

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ix3W549m-1645522338219)(image/202109192009809.png)]

只查询第二列和第三列,gstate、gprice,进行了全表扫描,因为中间跳过了索引的第一列

EXPLAIN
SELECT * FROM goods WHERE gstate=1 AND gprice=35

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lcgX7Xru-1645522338219)(image/202109192013217.png)]

只查询第一列和第三列,gcount、gprice,虽然使用了索引,但是索引的长度只使用了一个索引,也就是第一列的索引,并没有使用第三列的索引

EXPLAIN
SELECT * FROM goods WHERE gcount=20 AND gprice=35

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G2jRAhpY-1645522338220)(image/202109192015022.png)]

3、不在索引列上做任何的操作(计算、函数、类型转换),会导致索引失效,全表扫描

4、不在索引列查询条件前面使用范围查询><between and),也就是范围查询后的索引全部失效

5、查询结果尽量覆盖索引,也就是,尽量只查询建立索引的字段,少使用select *

6、在MySQL中使用除等于=以外的运算符,会导致索引失效,在MySQL8以后,查询计划的type为all

7、is nullis not null会导致索引失效

8、like中通配符位置除了在右边,其他都会索引失效,即like '%M'like '%M%'

9、字符串不加单引号,会索引失效,MySQL引擎会进行类型转换,和第三条意思一样

10、使用or连接,会导致索引失效

sql分页:减少应用压力

Oracle通过使用rownum伪列来实现分页

你可能感兴趣的:(jdk,java基础,配置文件,数据库,mysql)