数据库
MySQL是一种开放源代码的关系型数据库管理系统
DB:数据库,存储数据的“仓库”。
DBMS:数据库管理系统
SQL:结构化查询语言( Structure Query Language ):专门用来与数据库通信的语
言。
安装
具体安装步骤:https://blog.csdn.net/qq_44891948/article/details/104167874
配置相关
1. 连接服务端的命令
mysql -uroot -p
2. 退出
exit
3. 查看当前具体进程
tasklist
tasklist |findstr mysqld
4. 杀死具体进程(只有在管理员cmd窗口下才能成功)
taskkill /F /PID PID号
5. 设置密码
mysqladmin -uroot -p原密码 password 新密码
6. 破解密码
# 1 先关闭当前mysql服务端 命令行的方式启动(让mysql跳过用户名密码验证功能)
mysqld --skip-grant-tables
# 2 直接以无密码的方式连接
mysql -uroot -p 直接回车
# 3 修改当前用户的密码
update mysql.user set password=password(123456) where user='root' and host='localhost';
# 4 立刻将修改数据刷到硬盘
flush privileges;
# 5 关闭当前服务端 然后以正常校验授权表的形式启动
数据库相关命令
--1. 创建数据库
create DATABASE 表名 default charset=utf8;
create database test default charset=utf8;
--2. 查看所有数据库
show databases;
--3. 切换数据库
use 数据库名;
--4.删除数据库
drop 数据库名;
--5. 查看数据库中所有的表
show tables;
--6. 查看表信息
describe userInfo;
--7. 查看表结构
desc 表名;
--8. 查看创建表sql语句
show create table 表名;
--9. 查看库的描述
show create database 库名;
--7 .查看用户
select user,host from mysql.user;
--8. 创建数据库用户[这个用户只能看到这个数据库]
create user luffy_user identified by 'luffy';
grant all privileges on mall.* to 'mall'@'%' identified by 'mall123';
flush privileges;
练习数据库
-- 创建数据库
create database test default charset=utf8;
-- 查看所有数据库
show databases;
-- 删除数据库
drop database test;
-- 切换数据库
use test;
-- 查看数据库中所有的表
show databases;
-- 查看数据库描述
show create database test
-- 查看当前所有数据库的用户
select user, host from mysql.user;
-- 给test数据库创建用户,只允许他看到该数据库
create user test_001 identified by 'test';
grant all privileges on test.*to 'test'@'%' identified by 'test123';
flush privileges;
数据库表
-- 创建数据库表
create table userInfo(
name VARCHAR(100),
age int
)ENGINE=INNODB default CHARSET=utf8;
-- 查看数据库中所有的表
show tables;
-- 查看表信息
describe userInfo;
-- 查看表结构
desc userInfo;
-- 查看创建表sql语句
show create table userInfo;
-- 删除数据库表
drop table userInfo;
- DDL 定义
- DML 操作:增删改
- DQL 查询
- DCL 控制
操作数据库>操作数据库表>操作数据库表中的数据
--1. 创建数据库表
语法
create table 表名(
字段名1 类型(宽度) 约束条件,
字段名2 类型(宽度) 约束条件,
字段名3 类型(宽度) 约束条件
)ENGINE=INNODB default CHARSET=utf8;
测试
create table userInfo(
name VARCHAR(100),
age int
);
--2. 删除数据库
drop userInfo;
数据类型
数值
- tinyint 十分小的数据 1个字节
- smallint 较小的数据 2个字节
- mediumint 中等大小的数据 3个字节
- int 标准的整数 4个字节 常用
- bigint 超大的数据 8个字节
- float 浮点数 4个字节
- double 浮点数 8个字节
- decimal 字符串形式的浮点数 常用(计算金额)
# 存储限制
float(255,30) # 总共255位 小数部分占30位
double(255,30) # 总共255位 小数部分占30位
decimal(65,30) # 总共65位 小数部分占30位
# 精确度验证
create table t15(id float(255,30));
create table t16(id double(255,30));
create table t17(id decimal(65,30));
insert into t15 values(1.111111111111111111111111111111);
insert into t16 values(1.111111111111111111111111111111);
insert into t17 values(1.111111111111111111111111111111);
float < double < decimal
# 要结合实际应用场景 三者都能使用
字符类型
- char 字符串固定大小 0~255
- varchar 可变字符串 0~65535 常用
- tinytext 微型文本 2^8 - 1
- text 文本串 2^16 - 1 常用
时间类型
- date: 日期格式 YYYY-MM-DD 年月日 2020-5-4
- datetime: 时间格式 YYYY-MM-DD HH: mm: ss 年月日时分秒 2020-5-4 11:11:11
- time: 时间格式 HH: mm: ss 时分秒11:11:11
- Year:2020
- timestamp 时间戳 1970.1.1到现在的毫秒数
枚举与集合类型
- 枚举(enum) 多选一
- 集合(set) 多选多
字段属性
- default默认值
- 非空 null not null
- unique唯一
- primary key主键
- auto_increment自增
- foreign key外键
- comment: 注释
存储引擎
日常生活中文件格式有很多中,并且针对不同的文件格式会有对应不同存储方式和处理机制(txt,pdf,word,mp4...)
针对不同的数据应该有对应的不同的处理机制来存储
存储引擎就是不同的处理机制
MySQL主要存储引擎
-
Innodb
是MySQL5.5版本及之后默认的存储引擎
存储数据更加的安全
-
myisam
是MySQL5.5版本之前默认的存储引擎
速度要比Innodb更快 但是我们更加注重的是数据的安全
-
memory
内存引擎(数据全部存放在内存中) 断电数据丢失
-
blackhole
无论存什么,都立刻消失(黑洞)
"""
# 查看所有的存储引擎
show engines;
# 不同的存储引擎在存储表的时候 异同点
create table t1(id int) engine=innodb;
create table t2(id int) engine=myisam;
create table t3(id int) engine=blackhole;
create table t4(id int) engine=memory;
# 存数据
insert into t1 values(1);
insert into t2 values(1);
insert into t3 values(1);
insert into t4 values(1);
"""
修改表
# MySQL对大小写是不敏感的
"""
1 修改表名
alter table 表名 rename 新表名;
2 增加字段
alter table 表名 add 字段名 字段类型(宽度) 约束条件;
alter table 表名 add 字段名 字段类型(宽度) 约束条件 first;
alter table 表名 add 字段名 字段类型(宽度) 约束条件 after 字段名;
3 删除字段
alter table 表名 drop 字段名;
4 修改字段
alter table 表名 modify 字段名 字段类型(宽度) 约束条件;
alter table 表名 change 旧字段名 新字段名 字段类型(宽度) 约束条件;
论:
change用来字段重命名。不能修改字段类型和约束;
modify不用来字段重命名,只能修改字段类型和约束;
"""
复制表
"""
我们sql语句查询的结果其实也是一张虚拟表
"""
create table 表名 select * from 旧表; 不能复制主键 外键 ...
create table new_dep2 select * from dep where id>3;
表关系
表与表之间最多只有四种关系
一对多关系
多对多关系
一对一关系
没有关系
一对多
"""
怎么判断表与表之间的关系
换位思考,分别站在两张表的角度考虑
员工与部门表为例
先站在员工表
一个员工能否对应多个部门(一条员工数据能否对应多条部门数据)
不能
再站在部门表
思考一个部门能否对应多个员工(一个部门数据能否对应多条员工数据)
结论
员工表与部门表示单向的一对多
所以表关系就是一对多
"""
foreign key
1 一对多表关系 外键字段建在多的一方
2 在创建表的时候 一定要先建被关联表
3 在录入数据的时候 也必须先录入被关联表
测试
-- 部门
create table dep(
id int primary key auto_increment,
dep_name char(16),
dep_desc char(32)
);
-- 员工
create table emp(
id int primary key auto_increment,
name char(16),
gender enum('male','female','others') default 'male',
dep_id int,
foreign key(dep_id) references dep(id)
);
insert into dep(dep_name,dep_desc) values('sb教学部','教书育人'),('外交部','多人外交'),('nb技术部','技术能力有限部门');
insert into emp(name,dep_id) values('jason',2),('egon',1),('tank',1),('kevin',3);
# 修改dep表里面的id字段
update dep set id=200 where id=2; 不行
# 删除dep表里面的数据
delete from dep; 不行
# 1 先删除教学部对应的员工数据 之后再删除部门
操作太过繁琐
# 2 真正做到数据之间有关系
更新就同步更新
删除就同步删除
"""
级联更新 >>> 同步更新
级联删除 >>> 同步删除
"""
create table dep(
id int primary key auto_increment,
dep_name char(16),
dep_desc char(32)
);
create table emp(
id int primary key auto_increment,
name char(16),
gender enum('male','female','others') default 'male',
dep_id int,
foreign key(dep_id) references dep(id)
on update cascade # 同步更新
on delete cascade # 同步删除
);
insert into dep(dep_name,dep_desc) values('sb教学部','教书育人'),('外交部','多人外交'),('nb技术部','技术能力有限部门');
insert into emp(name,dep_id) values('jason',2),('egon',1),('tank',1),('kevin',3);
多对多
"""
图书表和作者表
"""
针对多对多字段表关系 不能在两张原有的表中创建外键
需要你单独再开设一张 专门用来存储两张表数据之间的关系
"""
create table book(
id int primary key auto_increment,
title varchar(32),
price int
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int
);
create table book2author(
id int primary key auto_increment,
author_id int,
book_id int,
foreign key(author_id) references author(id)
on update cascade # 同步更新
on delete cascade, # 同步删除
foreign key(book_id) references book(id)
on update cascade # 同步更新
on delete cascade # 同步删除
);
一对一
"""
id name age addr phone hobby email........
如果一个表的字段特别多 每次查询又不是所有的字段都能用得到
将表一分为二
用户表
用户表
id name age
用户详情表
id addr phone hobby email........
站在用户表
一个用户能否对应多个用户详情 不能!!!
站在详情表
一个详情能否属于多个用户 不能!!!
结论:单向的一对多都不成立 那么这个时候两者之间的表关系
就是一对一
或者没有关系(好判断)
客户表和学生表
在你们报名之前你们是客户端
报名之后是学生(期间有一些客户不会报名)
"""
一对一 外键字段建在任意一方都可以 但是推荐你建在查询频率比较高的表中
create table authordetail(
id int primary key auto_increment,
phone int,
addr varchar(64)
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int,
authordetail_id int unique,
foreign key(authordetail_id) references authordetail(id)
on update cascade # 同步更新
on delete cascade # 同步删除
)
自连接
create table category(
categoryid int(10) primary key auto_increment comment '主题id',
categoryname varchar(50) not null comment '主题名字',
pid int(10) not null comment '父id'
)
insert into category values(1,'文科',0),(2,'历史',1),(3,'高中历史',2),(4,'地理',1),(5,'初中历史',2);
总结
表关系的建立需要用到foreign key
一对多
外键字段建在多的一方
多对多
自己开设第三张存储
一对一
建在任意一方都可以 但是推荐你建在查询频率较高的表中
增删改
增
--1.增
INSERT INTO 表名(列名1,列名2, …) VALUES(值1, 值2)
--因为没有指定要插入的列,表示按创建表时列的顺序插入所有列的值:
INSERT INTO stus VALUES('s_1002', 'liSi', 32, 'female');
改
--2.改
--2.1单表修改
UPDATE 表名 SET 列名1=值1, … 列名n=值n [WHERE 条件]
--2.2多表修改
UPDATE 表1 【inner】 john 表2 on 表 SET 列名1= 新值1,列名2 =新值2
【where 筛选条件】
删
--3.删
--3.1单表删除两种语法
DELETE FROM 表名 [WHERE 条件]
TRUNCATE TABLE 表名;
--3.2多表删除语法
DELETE FROM 表1 别名1 INNER JOIN 表2 别名2 on 连接条件 【AND 筛选条件】
总结
-- 处理效率:drop>trustcate>delete
-- drop删除整个表,
-- trustcate删除表中所有数据,但不删除表,
-- delete删除部分记录
-- TRUNCATE删除的记录是无法回滚的,但DELETE删除的记录是可以回滚的.
--TRUNCATE是先DROP TABLE,再CREATE TABLE
查询
SELECT selection_list /*要查询的列名称*/
FROM table_list /*要查询的表名称*/
WHERE condition /*行条件*/
GROUP BY grouping_columns /*对结果分组*/
HAVING condition /*分组后的行条件*/
ORDER BYsorting_columns /*对结果分组*/
LIMIT offset_start, row_count /*结果限定*/
数据查询操作: select 列名 from 表名 where
操作符: and or in between and is null is not null not
模糊查询:like
% 匹配任意多个字符
_ 匹配任意单个字符
查询结果处理:算术运算:
常用函数:
concat()
substring()
length()
upper()
lower()
date()
year()
month()
day()
adddate()
datediff()
聚集函数:
count():统计指定列不为NULL的记录行数
avg():计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0
max():计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算
min():计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算
sum():计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0;
分组查询: group by
过滤分组: having
几个重要关键字的执行顺序
# 书写顺序
select id,name from emp where id > 3;
# 执行顺序
from
where
select
where筛选条件
-- where 筛选条件
# 作用:是对整体数据的一个筛选操作
# 1.查询id大于等于3小于等于6的数据
select * from emp where id between 3 and 6;
select * from emp where id >=3 and id<=6;
# 2.查询薪资是20000或者18000或者17000的数据
select * from emp where salary in (17000, 18000, 20000);
# 3.查询员工姓名中包含字母o的员工的姓名和薪资
select name,salary from emp where name like '%o%';
# 4.查询员工姓名是由四个字符组成的 姓名和薪资 char_length() _
select name,salary from emp where name like '____';
# 5.查询id小于3或者id大于6的数据
select * from emp where id < 3 or id>6;
# 6.查询薪资不在20000,18000,17000范围的数据
select * from emp where salary not in (17000, 18000, 20000);
# 7.查询岗位描述为空的员工姓名和岗位名 针对null不用等号 用is
select name,post from emp where post_comment is null;
算术运算
常用函数:
concat()
substring()
length()
upper()
lower()
date()
year()
month()
day()
adddate()
datediff()
聚集函数:
count():统计指定列不为NULL的记录行数
avg():计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0
max():计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算
min():计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算
sum():计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0;
group by分组
"""
分组之后 最小可操作单位应该是组 不再是组内的单个数据
上述命令在你没有设置严格模式的时候是可正常执行的 返回的是分组之后 每个组的第一条数据 但是这不符合分组的规范:分组之后不应该考虑单个数据 而应该以组为操作单位(分组之后 没办法直接获取组内单个数据)
"""
设置严格模式之后 分组 默认只能拿到分组的依据
select post from emp group by post;
按照什么分组就只能拿到分组 其他字段不能直接获取 需要借助于一些方法(聚合函数)
# 1 按照部门分组
select post from emp group by post;
# 1.获取每个部门的最高薪资
select post, max(salary) from emp group by post;
# 2.获取每个部门的最低薪资
select post, min(salary) from emp group by post;
# 3.获取每个部门的平均薪资
select post, avg(salary) from emp group by post;
# 4.获取每个部门的工资总和
select post, sum(salary) from emp group by post;
# 5.获取每个部门的人数
select post, count(id) from emp group by post;
# 6.查询分组之后的部门名称和每个部门下所有的员工姓名
# group_concat不单单可以支持你获取分组之后的其他字段值 还支持拼接操作
select post,group_concat(name) from emp group by post
select post,group_concat(name,'_DSB') from emp group by post;
分组注意事项
# 关键字where和group by同时出现的时候group by必须在where的后面
where先对整体数据进行过滤之后再分组操作
where筛选条件不能使用聚合函数
having分组之后的筛选条件
"""
having的语法根where是一致的
只不过having是在分组之后进行的过滤操作
即having是可以直接使用聚合函数的
"""
# 统计各部门年龄在30岁以上的员工平均工资并且保留平均薪资大于10000的部门
select post from emp where age>30 group by post having avg(salary)>10000;
distinct去重
"""
一定要注意 必须是完全一样的数据才可以去重!!!
一定不要将主键忽视了 有主键存在的情况下 是不可能去重的
"""
select distinct age from emp;
order by排序
"""
order by默认是升序 asc 该asc可以省略不写
也可以修改为降序 desc
"""
-- 统计各部门年龄在30岁以上的员工平均工资并且保留平均薪资大于1000的部门,然后对平均工资降序排序
select post,avg(salary) from emp where age>30 group by post having avg(salary)>1000 order by avg(salary) desc;
limit限制展示条数
limit 【起始条目索引,】查询的条目数
起始条目索引从0开始
select * from emp limit 0,5;
多表操作
# inner join 内连接 只拼接两张表中共有的数据部分
# left join 左连接 左表所有的数据都展示出来 没有对应的项就用NULL
# right join 右连接 右表所有的数据都展示出来 没有对应的项就用NULL
# union 全连接 左右两表所有的数据都展示出来
子查询
"""
子查询就是我们平时解决问题的思路
分步骤解决问题
第一步
第二步
...
将一个查询语句的结果当做另外一个查询语句的条件去用
"""
# 查询部门是技术或者人力资源的员工信息
1 先获取部门的id号
2 再去员工表里面筛选出对应的员工
select id from dep where name='技术' or name = '人力资源';
select name from emp where dep_id in (200,201);
select * from emp where dep_id in (select id from dep where name='技术' or name = '人力资源');
自连接查询
create table category(
categoryid int(10) primary key auto_increment comment '主题id',
categoryname varchar(50) not null comment '主题名字',
pid int(10) not null comment '父id'
)
insert into category values(1,'文科',0),(2,'历史',1),(3,'高中历史',2),(4,'地理',1),(5,'初中历史',2);
select * from category as a,category as b where a.categoryid = b.pid;
事务
1. 什么是事务
"""
开启一个事务可以包含多条sql语句
这些sql语句要么同时成功
要么一个都别想成功 称之为事务的原子性
"""
2.事务的作用
"""
保证了对数据操作的安全性
"""
3.事务四大特性
ACID
A:原子性
一个事务是一个不可分割的单位,事务中包含的诸多操作
要么同时成功要么同时失败
C:一致性
事务必须是使数据库从一个一致性的状态变到另外一个一致性的状态
一致性跟原子性是密切相关的
I:隔离性
一个事务的执行不能被其他事务干扰
(即一个事务内部的操作及使用到的数据对并发的其他事务是隔离的,并发执行的事务之间也是互相不干扰的)
D:持久性
也叫"永久性"
一个事务一旦提交成功执行成功 那么它对数据库中数据的修改应该是永久的
接下来的其他操作或者故障不应该对其有任何的影响
4.如何使用事务
# 事务相关的关键字
# 1 开启事务
start transaction;
# 2 回滚(回到事务执行之前的状态)
rollback;
# 3 确认(确认之后就无法回滚了)
commit;
"""模拟转账功能"""
create table user(
id int primary key auto_increment,
name char(16),
balance int
);
insert into user(name,balance) values
('jason',1000),
('egon',1000),
('tank',1000);
# 1 先开启事务
start transaction;
# 2 多条sql语句
update user set balance=900 where name='jason';
update user set balance=1010 where name='egon';
update user set balance=1090 where name='tank';
commit;
rollback;
"""
总结
当你想让多条sql语句保持一致性 要么同时成功要么同时失败
你就应该考虑使用事务
"""
索引
ps:数据都是存在与硬盘上的,查询数据不可避免的需要进行IO操作
索引:就是一种数据结构,类似于书的目录。意味着以后在查询数据的应该先找目录再找数据,而不是一页一页的翻书,从而提升查询速度降低IO操作
索引在MySQL中也叫“键”,是存储引擎用于快速查找记录的一种数据结构
- primary key
- unique key
- index key
注意foreign key不是用来加速查询用的,不在我们的而研究范围之内
上面的三种key,前面两种除了可以增加查询速度之外各自还具有约束条件,而最后一种index key没有任何的约束条件,只是用来帮助你快速查询数据
本质
通过不断的缩小想要的数据范围筛选出最终的结果,同时将随机事件(一页一页的翻)
变成顺序事件(先找目录、找数据)
也就是说有了索引机制,我们可以总是用一种固定的方式查找数据
一张表中可以有多个索引(多个目录)
索引虽然能够帮助你加快查询速度但是也有缺点
"""
1 当表中有大量数据存在的前提下 创建索引速度会很慢
2 在索引创建完毕之后 对表的查询性能会大幅度的提升 但是写的性能也会大幅度的降低
"""
索引不要随意的创建!!!
b+树
"""
只有叶子节点存放的是真实的数据 其他节点存放的是虚拟数据 仅仅是用来指路的
树的层级越高查询数据所需要经历的步骤就越多(树有几层查询数据就需要几步)
一个磁盘块存储是有限制的
为什么建议你将id字段作为索引
占得空间少 一个磁盘块能够存储的数据多
那么久降低了树的高度 从而减少查询次数
"""
聚集索引(primary key)
"""
聚集索引指的就是主键
Innodb 只有两个文件 直接将主键存放在了idb表中
MyIsam 三个文件 单独将索引存在一个文件
"""
辅助索引(unique,index)
查询数据的时候不可能一直使用到主键,也有可能会用到name,password等其他字段
那么这个时候你是没有办法利用聚集索引。这个时候你就可以根据情况给其他字段设置辅助索引(也是一个b+树)
"""
叶子节点存放的是数据对应的主键值
先按照辅助索引拿到数据的主键值
之后还是需要去主键的聚集索引里面查询数据
"""
覆盖索引
在辅助索引的叶子节点就已经拿到了需要的数据
# 给name设置辅助索引
select name from user where name='jason';
# 非覆盖索引
select age from user where name='jason';
测试索引是否有效的代码
感兴趣就自己试一试 不感兴趣直接忽略
**准备**
```mysql
#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_insert1G
#4. 调用存储过程
call auto_insert1();
```
``` mysql
# 表没有任何索引的情况下
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字段一剑封喉
```
#### 联合索引
```mysql
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'; # 速度变快
```
总结:上面这些操作,你感兴趣可以敲一敲,不感兴趣你就可以不用敲了,权当看个乐呵。理论掌握了就行了
慢查询日志
设定一个时间检测所有超出该时间的sql语句,然后针对性的进行优化!