day01——数据库前戏、MySQL简介和基本使用
存取数据的演变史
1.文本文件
文件路径不固定:C:\aaa.txt D:\bbb.txt E:\ccc.txt
数据格式不统一:jason|123 jason$123 jason 123
2.软件开发目录规范
规定了数据应该保存在db目录下>>>:路径偏向统一
db/user.txt db/userinfo.txt db/jason.json db/jason
数据格式还是没有得到统一
文本 json格式 对象
db文件夹里
当数据量多的时候,会产生很多的文件,多次读取文件会占用大量的资源
3.数据库服务(重点)
统一路径 统一操作方式
'''数据库能解决以上你所有遇到的问题'''
数据存储发展史
1.单机游戏
数据存储于各个计算机本地 不能实现数据的共享
2.网络游戏
数据存储于网络中 可以共享(数据库服务) 数据库一定是基于网络的!!!
'''以上所有遇到的问题核心都是基于数据的'''
数据库的本质
1.站在底层原理的角度
数据库指的是操作数据的进程(一堆代码)
2.站在实际应用的角度
数据库指的是可视化操作界面(一些软件)
ps:以后不做特殊说明的情况下讲数据库其实指的是数据库软件
'''数据库软件本质也是cs架构的程序'''
CS架构的软件就必须有服务端和客户端,理论上来说,我们每个人都能写一款数据库出来,只不过你写的数据库没人用,因此,市面上有很多牛逼的程序员,就写了很多的数据库软件,所以,你也会发现,现在市场上的确有很多的数据库
数据库的分类
'''市面上有很多的数据库,但是大致分为两大类'''
1.关系数据库
特征1:拥有固定的表结构(字段名 字段类型)
id name pwd
特征2:数据之间可以建立数据库层面关系
用户表数据
豪车表数据
豪宅表数据
MySQL、Oracle、SQLserver、PostgreSQL、sqlite、MariaDB、db2、access
1.MySQL:开源免费 使用最广 性价比贼高
2.Oracle:收费 使用成本较高但是安全性也最高
3.PostgreSQL:开源免费 支持二次开发 兼容性极高
4.MariaDB:跟MySQL是一个作者 开源免费
5.sqlite:小型数据库 主要用于本地测试
2.非关系型数据库
特征1:没有固定的表结构 数据存储采用K:V键值对的形式
{'name':'jason'}
{'username':'kevin','password':123}
特征2:数据之间无法建立数据库层面的关系
可以自己编写代码建立逻辑层面的关系
redis、MongoDB、memcache
1.Redis:目前最火 使用频率最高的非关系型数据库(缓存数据库)
虽然缓存数据库是基于内存做数据存取但是拥有持久化的功能
2.MongDB:文档型数据库 最像关系型数据库的非关系型数据库
主要用在爬虫以及大数据领域
3.memcache:已经被Redis淘汰
"""
虽然数据库软件有很多 但是操作方式大差不差 学会了一个几乎就可以学会所有
其中以MySQL最为典型
"""
SQL和NoSQL
数据库服务端是可以服务多种类型的客户端
客户端可以是自己开发的 也可以是python代码编写 也可以是其他语言编写
SQL
操作关系型数据库的语言
NoSQL
操作非关系型数据库的语言
ps:要想跟数据库交互就必须使用数据库指定的语言
"""
SQL有时候也指代关系型数据库
NoSQL有时候也指代非关系型数据库
"""
MySQL简介
1.版本问题
# 4.x之前的版本直接忽略,不够稳定
5.6:老项目用的多,是最稳定的版本
5.7:新老项目都在用
8.x:最新版(一般最新版不推荐使用)
'''在IT界,生产环境中尽量不要使用最新版本,测试环境中用哪个版本都可以'''
# 任何软件,生产环境中,都要使用稳定版本,而不是追求最新版本
"""
如果你治安的电脑装过MySQL,而卸载的方式又不对,就有可能存在很多的残留垃圾文件,如果不删除掉,就有可能导致新的MySQL装不上,装上之后,有可能启动不起来,反正就是各种问题!!
"""
# 如果你的电脑中确实存在了残留文件,怎么办呢?
利用一些杀毒软件,去检测你电脑上的残留文件,然后把检测出来的垃圾文件全部删除即可
2.下载流程
1.访问官网
2.点击DOWNLOADS并点击GPL
3.点击community server
4.点击Archives
5.选择对应系统的对应版本下载即可(zip压缩包)
3.主要目录介绍
bin目录
存放启动文件
mysql.exe(服务端) mysql.exe(客户端)
data目录
存放核心数据
my-default.ini
默认的配置文件
readme
软件说明
如何使用MySQL
1.先启动服务端,要去到bin路径下找到mysqld.exe启动文件
2.启动客户端(新建一个cmd窗口出来),连接服务端(服务端的cmd窗口不能关闭)
3.ctrl + c是停止服务的
4.把mysql服务端所在的路径加入到环境变量中
5.制作系统服务
# 目的:就是可以随时关闭mysql的服务端,达到开机自启动,自关闭
1.先关闭已经启动的服务端
2.在打开cmd窗口:
mysqld --install (Install/Remove of the Service Denied!) # 没有权限
'''使用管理员方式启动cmd'''
# 第一次安装成功之后,服务并没有启动,需要手动启动一次
3.启动服务的方式:
1.鼠标点击启动按钮
2.net start mysql # (管理员权限)
4.停止服务
1.鼠标点击停止
2.net stop mysql # (管理员权限)
5.如何卸载服务
1.先关闭服务
2.mysql --remove
# 如何退出客户端:exit
"""
打开服务的方式:
1. 在底部任务栏右键选择任务管理器 >>>>> 服务
2. 此电脑右键 >>> 管理 >>> 服务和应用程序 >>> 服务
3. win + r >>> 输入:services.msc >>> 回车
"""
"""
直接使用mysql命令默认是游客模式 权限和功能都很少
mysql -u用户名 -p密码
管理员默认没有密码 连续回车即可
mysql -uroot -p
"""
密码相关操作
1.修改管理员密码
方式一:mysqladmin
mysqladmin -u 用户名 -p 原密码 password 新密码
方式二:直接修改存储用户数据的表
方式三:冷门操作 有些版本可能还不支持
set password=password('新密码') # 修改当前登录用户的密码
'''管理员密码改完之后,一定要使用管理员账号登录'''
2.忘记密码
方式1:卸载重新装
方式2:把data目录删除 拷贝别人的目录
方式3:小把戏操作
1.先停止服务端
net stop mysql
2.用'跳过授权表'的形式再次启动服务端
mysql --skip-grant-tables
'跳过授权表'>>>:客户端在登录mysql的时候,只需要输入用户名即可,不再验证密码
3.用管理形式登录成功之后,进行修改密码
mysql -u root -p
# 下面的命令先了解
update mysql.user set password=password('123') where Host='localhost' and User='root';
4.退出客户端,终止服务端
5.不要在使用跳过授权表了,正常启动服务
数据库重要概念
"""
强调:小白阶段为了更加方便的理解 做了以下比喻 本质其实有一点点的区别
"""
库 >>> 文件夹
表 >>> 文件夹下的文件
记录 >>> 文件夹下的文件下的一行行的数据
验证
1.查看所有的库名称
show databases;
2.查看所有的表名称
show tables;
3.查看所有的记录
select * from mysql.user;
'''
information_schema库是mysql默认在内存中创建的库
'''
针对库的SQL语句
'''在mysql中,sql语句都要以分号结尾'''
'''基于库的增删改查'''
1.创建库
create database 库名;
2.查看库
show databases; # 查看所有的库名称
show create database 库名; # 查看指定库信息
3.编辑库
alter database 库名 charset='utf8';
4.删除库
drop database 库名;
5.查看当前在哪个库下
select database();
针对表的SQL语句
'''基于表的增删改查'''
操作表之前需要先确定库
create database db1;
切换操作库
use db1;
1.创建表
create table 表名(字段名 字段类型,字段名 字段类型);
2.查看表
show tables; 查看库下所有的表名称
show create table 表名; 查看指定表信息
describe 表名; 查看表结构
desc 表名;
ps:如果想跨库操作其他表 只需要在表名前加库名即可
desc mysql.user;
3.编辑表
alter table 表名 rename 新表明;
4.删除表
drop table 表名;
针对记录的SQL语句
'''基于记录的增删改查'''
1.插入数据
# 全字段添加
insert into 表名 values(数据值1,数据值2); # 单条数据
insert into 表名 values(数据值1,数据值2),(数据值1,数据值2),(数据值1,数据值2); # 多条数据
# 部分字段添加数据 需要在表明后面指定字段名
insert into 表名(字段1,字段2) values(数据值1,数据值2); # 单条数据
2.查询数据
select * from 表名; # 查询表中所有的数据
3.编辑数据
update 表名 set 字段名=新数据 where 筛选条件;
4.删除数据
delete from 表名;
delete from 表名 where id=2;
day02——数据库配置文件、存储引擎、字段类型
字符编码与配置文件
1.\s查看MySQL相关信息
当前用户、版本、编码、端口号
MySQL5.6及之前的版本编码需要人为统一 之后的版本已经全部默认统一
如果想要永久修改编码配置 需要操作配置文件
2.默认的配置文件是my-default.ini
拷贝上述文件并重命名为my.ini
直接拷贝字符编码相关配置即可无需记忆
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
ps:
1.utf8mb4能够存储表情 功能更强大
2.utf8与utf-8是有区别的 MySQL中只有utf8
修改了配置文件中关于[mysqld]的配置 需要重启服务端
"""
利用配置文件我们可以偷懒
将管理员登录的账号密码直接写在配置文件中 之后使用mysql登录即可
[mysql]
user='root'
password=123
"""
数据库存储引擎
'''存储引擎其实就是数据库存储数据的方式!!!'''
查看常见存储引擎的方式
show engines;
需要了解的四个存储引擎
MyISAM
MySQL5.6之前默认的存储引擎
存取数据的速度快 但是功能较少 安全性较低
InnoDB
MySQL5.6之后默认的存储引擎
支持事务、行锁、外键等操作 存取速度没有MyISAM快 但是安全性更高
Memory
基于内存存取数据 仅用于临时表数据存取
BlackHole
任何写入进去的数据都会立刻丢失
了解不同存储引擎底层文件个数
create database db2;
use db2;
create table t1(id int) engine=innodb
create table t2(id int) engine=myisam;
create table t3(id int) engine=memory;
create table t4(id int) engine=blackhole;
'''
1.innodb两个文件
.frm 表结构
.ibd 表数据(表索引)
2.myisam三个文件
.frm 表结构
.MYD 表数据
.MYI 表索引
3.memory一个文件
.frm 表结构
4.blackhole一个文件
.frm 表结构
'''
insert into t1 values(1);
insert into t2 values(1);
insert into t3 values(1);
insert into t4 values(1); # 表中没有数据
创建表的完整语法
create table 表名(
字段名 字段类型(数字) 约束条件,
字段名 字段类型(数字) 约束条件,
字段名 字段类型(数字) 约束条件,
);
1.字段名和字段类型是必须的
2.数字和约束条件是可选的
3.约束条件也可以写多个 空格隔开即可
4.最后一行结尾不能加逗号
ps:编写SQL语句报错之后不要慌 仔细查看提示 会很快解决
例如:near ')' at line 7
字段类型之整型
tinyint 1bytes 正负号(占1bit) 0-255 -> -128~127
smallint 1bytes 正负号(占1bit) 0-32 768
int 1bytes 正负号(占1bit)
bigint 1bytes 正负号(占1bit)
验证整型默认是否携带正负号
create table t5(id tinyint);
insert into t5 values(-129),(128);
结果是-128和127 也就意味着默认自带正负号
我们也可以取消正负号
create table t6(id tinyint unsigned);
insert into t6 values(-129),(128),(1000);
# 默认情况存储是带符号的,其他整型也是如此
严格模式
当我们在使用数据库存储数据的时候 如果数据不符合规范 应该直接报错而不是擅自修改数据 这样会导致数据的失真(没有实际意义)
正常都应该报错 但是我们之前不小心改了配置文件
show variables like '%mode%';
1.临时修改
set session sql_mode='strict_trans_tables';
在当前客户端有效
set global sql_mode='strict_trans_tables';
在当前服务端有效
2.永久修改
直接修改配置文件
字段类型之浮点型
float(20,10)
总共存储20位数 小数点后面占10
double(20,10)
总共存储20位数 小数点后面占10
decimal(20,10)
总共存储20位数 小数点后面占10
create table t7(id float(60,20));
create table t8(id double(60,20));
create table t9(id decimal(60,20));
insert into t7 values(1.11111111111111111111);
insert into t8 values(1.11111111111111111111);
insert into t9 values(1.11111111111111111111);
三者的核心区别在于精确度不同
float < double < decimal
字段类型之字符型
char
定长
char(4) 最多存储四个字符 超出就报错 不够四个空格填充至四个
varchar
变长
varchar(4) 最多存储四个字符 超出就报错 不够则有几位存几位
ps:char_length()获取字段存储的数据长度
默认情况下MySQL针对char的存储会自动填充空格和删除空格
set global sql_mode='strict_trans_tables,pad_char_to_full_length';
char VS varchar
char
优势:整存整取 速度快
劣势:浪费存储空间
varchar
优势:节省存储空间
劣势:存取数据的速度较char慢
数字的含义
数字在很多地方都是用来表示限制存储数据的长度
但是在整型中数字却不是用来限制存储长度
create table t12(id int(3)); 不是用来限制长度
insert into t12 values(12345);
create table t13(id int(5) zerofill); 而是用来控制展示的长度
insert into t13 values(123),(123456789);
create table t14(id int);
"""以后写整型无需添加数字"""
字段类型之枚举与集合
枚举
多选一
create table t15(
id int,
name varchar(32),
gender enum('male','female','others')
);
insert into t15 values(1,'tony','猛男'); # 建表时没有设置的无法插入
insert into t15 values(2,'jason','male');
insert into t15 values(3,'kevin','others');
集合
多选多(多选一)
create table t16(
id int,
name varchar(16),
hobbies set('basketabll','football','doublecolorball')
);
insert into t16 values(1,'jason','study'); # 建表时没有设置的无法插入
insert into t16 values(2,'tony','doublecolorball');
insert into t16 values(3,'kevin','doublecolorball,football');
字符类型之日期类型
datetime 年月日时分秒
date 年月日
time 时分秒
year 年
create table t17(
id int,
name varchar(32),
register_time datetime,
birthday date,
study_time time,
work_time year
);
insert into t17 values(1,'jason','2000-11-11 11:11:11','1998-01-21','11:11:11','2000');
ps:以后涉及到日期相关字段一般都是系统自动回去 无需我们可以操作
day03——非空、默认值、唯一、主键、外键、表关系(一对多、多对对、一对一)
无符号、零填充
unsigned
id int unsigned
zerofill
id int(5) zerofill
非空
create table t1(
id int,
name varchar(16)
);
insert into t1(id) values(1);
insert into t1(name) values('jason');
insert into t1(name,id) values('kevin',2);
ps:所有字段类型不加约束条件的情况下默认都可以为空
create table t2(
id int,
name varchar(16) not null
);
insert into t2(id) values(1); # 报错
insert into t2(name) values('jason');
insert into t2 values(1,'');
insert into t2 values(2,null);
默认值
create table t3(
id int default 666,
name varchar(32) default '匿名'
);
insert into t3(id) values(1);
insert into t3(name) values('jason');
insert into t3 values(2,'kevin');
唯一值
'''单列唯一'''
create table t4(
id int unique,
name varchar(32) unique
);
insert into t4 values(1,'jason'),(2,'jason'); # 报错
'''联合唯一'''
create table t5(
id int,
id varchar(32),
poot int,
unique(ip,poot)
);
insert into t5 values(1,'127.0.0.1',8080),(2,'127.0.0.1',8081),(3,'127.0.0.2',8080);
insert into t5 values(4,'127.0.0.1',8080);
主键
1.单从约束层面上而言主键相当于not null + unique(非空且唯一)
create table t6(
id int primary key,
name varchar(32)
);
insert into t6(name) values('jason'); # 报错
insert into t6 values(1,'kevin');
insert into t6 values(1,'jerry'); # 报错
2.InnoDB存储引擎规定了所有的表都必须有且只有一个主键(主键是组织数据的重要条件并且主键可以加快数据的查询速度)
2.1.当表中没有主键也没有其他非空且唯一字段的情况下
InnoDB会采用一个隐藏的字段作为表的主键 隐藏意味着无法使用 基于该表的数据查询 只能一行行查找 速度很慢
2.2.当表中没有主键但是有其他非空且唯一的字段 那么会从上往下将第一个该字段自动升级为主键
"""
我们在创建表的时候应该有一个字段用来标识数据的唯一性 并且该字段通常情况下就是'id'
create table userinfo(
id int primary key
);
"""
自增
该约束条件不能单独出现 并且一张表中只能出现一次 主要就是配合主键一起用
create table t8(
id int primary key;
name varchar(32)
);
create table t8(
id int primary key auto_increment,
name varchar(32)
);
"""
自增特性
自增不会因为数据的删除而回退 永远自增往前
如果自己设置了更大的数 则之后按照更大的往前自增
如果想重置某张表的主键值 可以使用
truncate 表名; 清空表数据并重置主键
"""
外键前戏
我们需要一张员工表
id name age dep_name dep_desc
1.表语义不明确(到底是员工还是部门)
2.存取数据过于冗余(浪费存储空间)
3.数据的扩展性极差
将上述表一分为二
id name age
id dep_name dep_desc
上述的三个问题全部解决 但是员工跟部门之间没有了关系
外键字段:用于标识数据之间关系的字段
关系的判断
表关系、数据关系其实意思是一样的 只是说法上有区分
关系总共有四种
一对多
多对多
一对一
没有关系
关系的判断可以采用'换位思考'原则
一对多关系
以员工表和部门表为例
1.先站在员工表的角度
问:一名员工能否对应多个部门
答:不可以
2.再站在部门表的角度
问:一个部门能否对应多名员工
答:可以
结论:一个可以一个不可以 那么关系就是'一对多'
针对'一对多'关系 外键字段建在'多'的一方
外键字段的建立
小技巧:先定义出含普通字段的表 之后再考虑外键字段的添加
create table emp(
id int primary key auto_increment,
name varchar(32),
age int,
dep_id int,
foreign key(dep_id) references dep(id)
);
create table dep(
id int primary key auto_increment,
dep_name varchar(32),
dep_desc varchar(64)
);
"""
1.创建表的时候一定要先创建被关联表
2.录入表数据的时候一定要先录入被关联表
3.修改数据的时候外键字段无法修改和删除
"""
针对3有简化措施>>>:级联更新级联删除
create table emp1(
id int primary key auto_increment,
name varchar(32),
age int,
dep_id int,
foreign key(dep_id) references dep1(id)
on update cascade
on delete cascade
);
create table dep1(
id int primary key auto_increment,
dep_name varchar(32),
dep_desc varchar(32)
);
"""
外键其实是强耦合 不符合解耦合的特性
所以很多时候 实际项目中当表较多的情况 我们可能不会使用外键 而是使用代码建立逻辑层面的关系
"""
多对多关系
以书籍表与作者表为例
1.先站在书籍表的角度
问:一本书能否对应多个作者
答:可以
2.再站在作者表的角度
问:一个作者能否对应多本书
答:可以
结论:两个都可以 关系就是'多对多'
针对'多对多'不能在表中直接创建 需要新建第三张关系表
create table book(
id int primary key auto_increment,
title varchar(32),
price float(5,2)
);
create table author(
id int primary key auto_increment,
name varchar(32),
phone bigint
);
create table book2author(
id int primary key auto_increment,
author_id int,
foreign key(author_id) references author(id)
on update cascade
on delete cascade,
book_id int,
foreign key(book_id) references book(id)
on update cascade
on delete cascade
);
一对一关系
以用户表与用户详情表为例
1.先站在用户表的角度
问:一个用户能否对应多个用户详情
答:不可以
2.再站在用户详情表的角度
问:一个用户详情能否对应多个用户
答:不可以
结论:两个都可以 关系就是'一对一'或者没有关系
针对'一对一'外键字段建在任何一方都可以 但是推荐建在查询频率较高的表中
create table user(
id int primary key auto_increment,
name varchar(32),
detail_id int unique,
foreign key(detail_id) references userdetail
on update cascade
on delete cascade
);
create table userdetail(
id int primary key auto_increment,
phone bigint
);
day04——SQL查询关键字
SQL语句查询关键字
select
指定需要查询的字段信息
select * 查所有字段
select name 查name字段
select char_length(name) 支持对字段做处理
from
指定需要查询的表信息
from mysql.user # mysql库中的user表
from t1 # 当前库中的t1表
SQL语句中关键字的执行顺序和编写顺序并不是一直的 可能会错乱
eg:
select id,name from userinfo;
我们先写的select在写的from 但是执行的时候是先执行的from再执行select
对应关键字的编写顺便和执行顺序我们没必要过多的在意 熟练之后会非常自然的编写 我们只需要把注意力放在每个关键字的功能上即可
前期数据准备
create table emp(
id int primary key auto_increment,
name varchar(20) not null,
gender enum('male','female') not null default 'male', #大部分是男的
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),
office int, #一个部门一个屋子
depart_id int
);
#插入记录
#三个部门:教学,销售,运营
insert into emp(name,gender,age,hire_date,post,salary,office,depart_id) values
('jason','male',18,'20170301','浦东第一帅形象代言',7300.33,401,1), #以下是教学部
('tom','male',78,'20150302','teacher',1000000.31,401,1),
('kevin','male',81,'20130305','teacher',8300,401,1),
('tony','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jack','female',18,'20110211','teacher',9000,401,1),
('jenny','male',18,'19000301','teacher',30000,401,1),
('sank','male',48,'20101111','teacher',10000,401,1),
('哈哈','female',48,'20150311','sale',3000.13,402,2),#以下是销售部门
('呵呵','female',38,'20101101','sale',2000.35,402,2),
('西西','female',18,'20110312','sale',1000.37,402,2),
('乐乐','female',18,'20160513','sale',3000.29,402,2),
('拉拉','female',28,'20170127','sale',4000.33,402,2),
('僧龙','male',28,'20160311','operation',10000.13,403,3), #以下是运营部门
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬银','female',18,'20130311','operation',19000,403,3),
('程咬铜','male',18,'20150411','operation',18000,403,3),
('程咬铁','female',18,'20140512','operation',17000,403,3);
编写SQL语句的小技巧
针对select后面的字段名可以先用*占位往后写 最后再回来修改
在实际应用中select后面很少直接写* 因为*表示所有 当表中字段和数据都特别多的情况下非常浪费数据库资源
"""
SQL语句的编写类似于代码的编写 不是一蹴而就的 也需要反反复复的修修补补
"""
查询关键字之where筛选
# 1.查询id大于等于3小于等于6的数据
select * from emp where id >= 3 and id <= 6; 支持逻辑运算符
select * from emp where id between 3 and 6;
# 2.查询薪资是20000或者18000或者17000的数据
select * from emp where salary=20000 or salary=18000 or salary=17000;
select * from emp where salary in (20000,18000,17000);支持成员运算
# 3.查询id小于3大于6的数据
select * from emp where id<3 or id>6;
select * from emp where id not between 3 and 6;
# 4.查询员工姓名中包含字母o的员工姓名与薪资
条件不够精准的查询 称之为 模糊查询
模糊查询的关键字是
like
模糊查询的常用符号
% 匹配任意个数的任意字符
eg:
%o% o jason owen loo wwoww
%o o asdasdo asdo
_ 匹配单个个数的任意字符
_o_ aox wob iok
o_ oi ok ol
select * from emp where name like '%o%';
# 5.查询员工姓名是由四个字符组成的员工姓名与其薪资
select * from emp where name like '____';
select * from emp where char_length(name)=4;
# 6.查询岗位描述为空的员工名与岗位名 针对null不能用等号 只能用is
select * from emp where post_comment=null 不可以
select * from emp where post_comment is NULL; 可以
"""
在MySQL中也有很多内置方法 我们可以通过查看帮助手册学习
help 方法名
"""
查询关键字之group by分组
分组:按照指定的条件将单个单个的数据组成一个个整体
eg:
将班级学生按照性别分组
将全国人民按照名族分组
将全世界的人按照肤色分组
分组的目的是为了更好的统计相关数据
eg:
每个班级的男女比例
每个名族的总占比
每个部门的平均薪资
聚合函数
专门用于分组之后的数据统计
max\min\sum\avg\count
最大值、最小值、求和、平均值、计数
1.将员工数据按照部门分组
select * from emp group by post;
"""
MySQL5.6默认不会报错
set global sql_mode='strict_trans_tables,only_full_group_by'
MySQL5.7及8.0默认都会直接报错
原因是分组之后 select后面默认的只能直接填写分组的依据 不能再写其他字段
select post from emp group by post;
select age from emp group by age;
分组之后默认的最小单位就应该是组 而不应该再是组内的单个数据单个字段
"""
2.获取每个部分的最高工资
'''要不要分组我们完全可以从题目的需求中分析出来尤其是出现关键字 每个 平均'''
select post,max(salary) from emp group by post;
针对sql语句执行之后的结果 我们是可以修改字段名称的 关键字as 也可以省略
select post as '部门',max(salary) as '最高薪资' from emp group by post;
3.一次性获取部门薪资相关统计
select post,max(salary) '最高薪',min(salary) '最低薪',avg(salary) '平均薪资',sum(salary) '月支出' from emp group by post;
4.统计每个部门的人数
select post,count(id) from emp group by post;
5.统计每个部门的部门名称以及部门下的员工姓名
'''分组以外的字段无法直接填写 需要借助于方法'''
select post,name from emp group by post; # 报错
select post,group_concat(name) from emp group by post;
select post,group_concat(name,age) from emp group by post;
select post,group_concat(name,'|',age) from emp group by post;
select post,group_concat(name,'_NB') from emp group by post;
select post,group_concat('DSB_',name,'_NB') from emp group by post;
查询关键字之having过滤
having与where本质是一样多的 都是用来对数据做筛选
只不过where用在分组之前(首次筛选)
having用在分组之后(二次筛选)
1.统计各部门年龄在30岁以上的员工平均工资 并且保留大于10000的数据
'''
稍微复杂一点的SQL 跟写代码几乎一样 也需要提前想好大致思路
每条SQL的结果可以直接看成就是一张表 基于该表如果还想继续操作则直接在产生该表的SQL语句上添加即可
'''
步骤1:先筛选出所有年龄大于30岁的员工数据
select * from emp where age > 30;
步骤2:再对筛选出来的数据按照部门分组并统计平均薪资
select post,avg(salary) from emp where age > 30 group by post;
步骤3:针对分组统计之后的结果做二次筛选
select post,avg(salary) from emp where age > 30 group by post having avg(salary) > 10000;
查询关键字之distinct去重
去重有一个必须的条件也很容易被忽略的条件
数据必须一模一样才可以去重
select distinct id,age from emp;关键字针对的是多个字段组合的结果 # id是主键
select distinct age from emp;
select distinct age,post from emp
查询关键字之order by排序
1.可以是单个字段排序
select * from emp order by age; # 默认升序
select * from emp order by age asc; # 默认升序(asc可以省略)
select * from emp order by age desc; # 降序
2.也可以是多个字段排序
select * from emp order by age,salary desc; # 先按照年龄升序排 相同的情况下再按照薪资降序排
统计各部门年龄在10岁以上的员工平均工资,并且保留平均工资大于1000的部门,然后对平均工资进行排序
1.先筛选出所有年龄大于10岁的员工
select * from emp where age > 10;
2.再对他们按照部门分组统计平均薪资
select post,avg(salary) from emp where age > 10 group by post;
3. 针对分组的结果做二次筛选
select post,avg(salary) from emp where age > 10 group by post having avg(salary) > 1000;
4.最后按照指定字段排序
select post,avg(salary) from emp where age > 10 group by post having avg(salary) > 1000 order by avg(salary);
"""
当一条SQL语句中很多地方都需要使用聚合函数计算之后的结果 我们可以节省操作(主要是节省了底层运行效率 代码看不出来) 在聚合函数后加上 as 别名
select post,avg(salary) as avg_salary from emp where age > 10 group by post having avg_salary > 1000 order by avg_salary;
"""
查询关键字之limit分页
当表中数据特别多的情况下 我们很少会一次性或者扭曲所有的数据
很多网站也是做了分页处理 一次性只能看一点点
select * from emp limit 5; 直接限制展示的条数
select * from emp limit 5,5; 从第五条开始往后读取5条
查询工资最高的人的详细信息
'''千万不要惯性思维 一看到工资最高就想着用分组聚合'''
select * from emp order by salary desc limit 1;
查询关键字之regexp正则表达式
SQL语句的模糊匹配 如果用不习惯 也可以自己写正则匹配查询
select * from emp where name regexp '^j.*?(n|y)';
多表查询的思路
表数据准备
create table dep(
id int primary key auto_increment,
name varchar(20)
);
create table emp(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);
# 插入数据
insert into dep values
(200,'技术'),
(201,'人力资源'),
(202,'销售'),
(203,'运营'),
(205,'财务');
insert into emp1(name,sex,age,dep_id) values
('jason','male',18,200),
('dragon','female',48,201),
('kevin','male',18,201),
('nick','male',28,202),
('owen','male',18,203),
('jerry','female',18,204);
select * from emp,dep; # 会将两张表中所有的数据对应一遍
这个现象我们也称之为'笛卡尔积' 无脑的对应没有意义 应该将有关系的数据对应到一起才合理
基于笛卡尔积可以将部门编号与部门id相同的数据筛选出来
涉及到两张及以上的表时 字段很容易冲突 我们需要在字段前面加上表名来指定
select * from emp,dep where emp.dep_id=dep.id;
基于上述的操作就可以将多张表合并到一起然后一次性获取更多的数据
day05——多表查询、Navicat、多表查询练习题、pymysql操作
多表查询的两种方法
方式一:连表操作
inner join 内连接
select * from emp inner join dep on emp.dep_id=dep.id
只连接两张表中公有的数据部分
left join 左连接
select * from emp left join dep on emp.dep_id=dep.id
以左表为基准 展示左表所有的数据 如有没有对应项则用NULL填充
right join 右连接
select * from emp right join dep on emp.dep_id=dep.id
以右表为基准 展示右表所有的数据 如果没有对应项则用null填充
union 全连接
select * from emp left join dep on emp.dep_id=dep.id
union
select * from emp right join dep on emp.dep_id=dep.id;
以左右表为基准 展示所有的数据 各自没有的全部NULL填充
'''
学会了连表操作之后也就可以连接N多张表
思路:将拼接之后的表起别名当成一张表再去与其他表拼接 再起别名当一张表 再去与其他表拼接 其次往复即可
'''
方式二:子查询
将一条SQL语句括起来当成另一条SQL语句的查询条件
题目:求姓名是jason的员工部门名称
步骤1:先根据jsaon获取部门编号
select dep_id from emp where name='jason';
步骤2:再根据部门编号获取部门名称
select name from dep where id=200;
总结
select name from dep where id=(select dep_id from emp where name='jason');
"""
很多时候多表查询需要结合实际情况判断用哪种 更多时候甚至是相互配合使用
"""
小知识点补充说明
1.concat与concat_ws
concat用于分组之前的字段拼接操作
select concat(name,'$',sex) from emp;
concat_ws拼接多个字段并且中间的连接符一致
select concat_ws('|',name,sex,age,dep_id) from emp;
2.exists
sql1 exists sql2
sql2有结果的情况下才会执行sql1 否则不执行sql1 返回空数据
3.表相关SQL补充
alter table 表名 rename 新表名; # 修改表名
alter table 表名 add 字段名 字段类型(数字) 约束条件; # 添加字段
alter table 表名 add 字段名 字段类型(数字) 约束条件 after 已有字段;
alter table 表名 add 字段名 字段类型(数字) 约束条件 first;
alter table 表名 change 旧字段 新字段 字段类型(数字) 约束条件; # 修改字段
alter table 表名 modify 字段名 新字段类型(数字) 约束条件; # 修改字段类型
alter table 表名 drop 字段名; # 删除字段
可视化软件Navicat
第三方开发的用来充当数据库客户端的简单快捷的操作界面
无论第三方软件有多么的花里胡哨 底层的本质还是SQL
能够操作数据库的第三方可视化软件有很多 其中针对MySQL最出名的就是Navicat
1.浏览器搜索Navicat直接下载
版本很多、能够充当的数据库客户端也很多
2.破解方式
先试用在破解、直接下载破解版(老版本)、修改试用日期
3.常用操作
有些功能可能需要自己修改SQL预览
创建库、表、记录、外键
逆向数据库到模型、模型创建
新建查询可以编写SQL语句并自带提示功能
SQL语句注释语法
--、#、\**\
运行、转储SQL文件
多表查询练习题
"""
编写复杂的SQL不要想着一口气写完
一定要先明确思路 然后一步步写一步步查一步步补
"""
1、查询所有的课程的名称以及对应的任课老师姓名
4、查询平均成绩大于八十分的同学的姓名和平均成绩
7、查询没有报李平老师课的学生姓名
8、查询没有同时选修物理课程和体育课程的学生姓名
9、查询挂科超过两门(包括两门)的学生姓名和班级
-- 1、查询所有的课程的名称以及对应的任课老师姓名
# 1.先确定需要用到几张表 课程表 分数表
# 2.预览表中的数据 做到心中有数
-- select * from course;
-- select * from teacher;
# 3.确定多表查询的思路 连表 子查询 混合操作
-- SELECT course.cname,teacher.tname FROM course INNER JOIN teacher ON course.teacher_id=teacher.tid;
-- 4、查询平均成绩大于八十分的同学的姓名和平均成绩
# 1.先确定需要用到几张表 学生表 分数表
# 2.预览表中的数据
-- select * from student;
-- select * from score;
-- select sname,avg(num) from score inner join student on score.student_id=student.sid group by sname having avg(num) > 80;
-- 7、查询没有报李平老师课的学生姓名
# 1.先确定需要用到几张表 老师表 课程表 分数表 学生表
# 2.预览每张表的数据
# 3.确定思路 思路1:正向筛选 思路2:筛选所有报了李平老师课程的学生id 然后取反即可
# 步骤1 先获取李平老师教授的课程id
-- select tid from teacher where tname = '李平老师';
-- select cid from course where teacher_id = (select tid from teacher where tname = '李平老师');
# 步骤2 根据课程id筛选出所有报了李平老师的学生id
-- select distinct student_id from score where course_id in (select cid from course where teacher_id = (select tid from teacher where tname = '李平老师'))
# 步骤3 根据学生id去学生表中取反获取学生姓名
-- SELECT sname FROM student WHERE sid NOT IN(SELECT DISTINCT student_id FROM score WHERE course_id IN (SELECT cid FROM course WHERE teacher_id=(SELECT tid FROM teacher where tname='李平老师')));
-- 8、查询没有同时选修物理课程和体育课程的学生姓名(报了两门或者一门不报的都不算)
# 1.先确定需要的表 学生表 分数表 课程表
# 2.预览表数据
# 3.根据给出的条件确定起手的表
# 4.根据物理和体育筛选课程id
-- select cid from course where cname in ('物理','体育');
# 5.根据课程id筛选出所有跟物理 体育相关的学生id
-- select student_id from score where course_id in (select cid from course where cname in ('物理','体育'));
# 6.统计每个学生报了的课程数 筛选出等于1的
-- select student_id from score where score_id in (SELECT cid FROM course where cname in ('物理','体育')) group by student_id having count(course_id)=1;
# 7.子查询获取学生姓名即可
-- SELECT sname FROM student WHERE sid IN (SELECT student_id FROM score WHERE course_id IN (SELECT cid FROM course where cname in ('物理','体育')) GROUP BY student_id HAVING COUNT(course_id)=1);
-- 9、查询挂科超过两门(包括两门)的学生姓名和班级
# 1.先确定涉及到的表 分数表 学生表 班级表
# 2.预览表数据
-- select * from class
# 3.根据条件确定以分数表作为起手条件
# 步骤1 先筛选掉大于60的数据
-- select * from score where num < 60;
# 步骤2 统计每个学生挂科的次数
-- select student_id,count(course_id) from score where num < 60 group by student_id;
# 步骤3 筛选次数大于等于2的数据
-- select student_id from score where num < 60 group by student_id having count(course_id) >= 2;
# 步骤4 连接班级表与学生表 然后基于学生id筛选即可
-- select sname,caption from student inner join class on class.cid=student.class_id where sid in (select student_id from score where num < 60 group by student_id having count(course_id) >= 2);
python操作MySQL
pymysql模块
pip3 install pymysql
import pymysql
# 1.链接MySQL服务端
conn = pymysql.connect(
host='127.0.0.1',
post=3306,
user='root',
password='123456',
db='db8',
charset='utf8'
)
# 2.产生游标对象
# cursor = conn.cursor() # 括号内不填写额外参数 数据是元祖 指定性不强 [(),()]
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # [{},{}]
# 3.编写SQL语句
sql = 'select * from score;'
# 4.发送SQL语句
affect_rows = cursor.execute(sql) # execute也有返回值 接收的是SQL语句影响的行数
print(affect_rows)
# 5.获取sql语句执行之后的结果
res = cursor.fetchall()
print(res)
pymysql补充说明
1.获取数据
fetchall() # 获取所有结果
fetchone() # 获取结果集的第一个数据
fetchmany() # 获取指定数量的结果集
ps:注意三者都有类似于文件光标移动的特性
cursor.scroll(1,'relative') # 基于当前位置往后移动
cursor.scroll(2,'absolute') # 基于数据的开头往后移动
2.增删改查
autocommit=True # 针对 增删改 自动确认(直接配置)
conn.commit() # 针对 增删改 需要二次确认(代码确认)
day06——视图、触发器、事务、存储过程、函数、流程控制、索引
SQL注入问题
怪像1:输对用户名就可以登录成功
怪像2:不需要对的用户名和密码也可以登录成功
SQL注入:利用特殊符号的组合产生特殊的含义 从而避开正常的业务逻辑
select * from userinfo where name='jason' -- kasdjksajd' and pwd=''
select * from userinfo where name='xyz' or 1=1 -- aksdjasldj' and pwd=''
针对上述的SQL注入问题 核心在于手动拼接了关键数据 交给execute处理即可
sql = " select * from userinfo where name=%s and pwd=%s "
cursor.execute(sql, (username, password))
'''
补充说明 可以一次性添加多个
executemany(sql,[(),(),(),()...])
'''
视图
视图就是通过查询得到一张虚拟表,然后保存下来,下次直接使用
create view teacher2course as
select * from teacher inner join course on teacher.tid = course.teacher_id;
1.视图的表只能用来查询不能做其他增删改操作
2.视图尽量少用 会跟真正的表产生混淆 从而干扰操作者
触发器
达到某个条件之后自动触发执行
在MySQL中更加详细的说明是触发器:针对表继续增、删、改操作能够自动触发
主要有六种情况:增前、增后、删前、删后、改前、改后
create trigger 触发器的名字 before/after insert/update/delete on 表名 for each
row
begin
sql语句
end
1.触发器命名有一定的规律
tri_before_insert_t1
tri_after_delete_t2
tri_after_update_t2
2.临时修改SQL语句的结束符
因为有些操作中需要使用分号
触发器实际应用
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_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
('kevin','0755','ls -l /etc',NOW(),'yes'),
('kevin','0755','cat /etc/passwd',NOW(),'no'),
('kevin','0755','useradd xxx',NOW(),'no'),
('kevin','0755','ps aux',NOW(),'yes');
# 查询errlog表记录
select * from errlog;
# 查看所有的触发器
show triggers;
# 删除触发器
drop trigger tri_after_insert_cmd;
事务
事务的四大特性(ACID)
A:原子性
事务中的各项操作是不可分割的整体 要么同时成功要么同时失败
C:一致性
是数据库从一个一致性状态变道另一个一致性状态
I:隔离性
多个事务之间彼此不干扰
D:持久性
也称永久性,指永固事务一旦提交,它对数据库中数据的改变就应该是永久性的
create table user(
id int primary key auto_increment,
name char(32),
balance int
);
insert into user(name,balance) values ('jason',1000),('kevin',1000),('tank',1000);
# 修改数据之前先开启事务操作
start transaction;
# 修改操作
update user set balance=900 where name='jason'; #买支付100元
update user set balance=1010 where name='kevin'; #中介拿走10元
update user set balance=1090 where name='tank'; #卖家拿到90元
# 回滚到上一个状态
rollback;
# 开启事务之后,只要没有执行commit操作,数据其实都没有真正刷新到硬盘
commit;
"""
事务相关关键字
start transaction;
rollback
commit
savepoint
"""
# savepoint 使用保留点 保留点在事务处理完成(执行一条rollback或commit)后自动释放
savepoint savepoint_name; // 声明一个 savepoint
rollback to savepoint_name; // 回滚到savepoint
在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改
InnoDB支持所有隔离级别
set transaction isolation level 级别
1.read uncommitted(未提交读)
事务中的修改即使没有提交,对其他事务也都是可见的,事务可以读取未提交的数据,这一现象也称之为"脏读"
2.read committed(提交读)
大多数数据库系统默认的隔离级别
一个事务从开始直到提交之前所作的任何修改对其他事务都是不可见的,这种级别也叫做"不可重复读"
3.repeatable read(可重复读) # MySQL默认隔离级别
能够解决"脏读"问题,但是无法解决"幻读"
所谓幻读指的是当某个事务在读取某个范围内的记录时另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录会产生幻行,InnoDB和XtraDB通过多版本并发控制(MVCC)及间隙锁策略解决该问题
4.serializable(可串行读)
强制事务串行执行,很少使用该级别
MVCC只能在read committed(提交读)、repeatable read(可重复读)两种隔离级别下工作,其他两个不兼容(read uncommitted:总是读取最新 serializable:所有的行都加锁)
InnoDB的MVCC通过在每行记录后面保存两个隐藏的列来实现MVCC
一个列保存了行的创建时间
一个列保存了行的过期时间(或删除时间) # 本质是系统版本号
每开始一个新的事务版本号都会自动递增,事务开始时刻的系统版本号会作为事务的版本号用来和查询到的每行记录版本号进行比较
例如
刚插入第一条数据的时候,我们默认事务id为1,实际是这样存储的
username create_version delete_version
jason 1
可以看到,我们在content列插入了kobe这条数据,在create_version这列存储了1,1是这次插入操作的事务id。
然后我们将jason修改为jason01,实际存储是这样的
username create_version delete_version
jason 1 2
jason01 2
可以看到,update的时候,会先将之前的数据delete_version标记为当前新的事务id,也就是2,然后将新数据写入,将新数据的create_version标记为新的事务id
当我们删除数据的时候,实际存储是这样的
username create_version delete_version
jason01 2 3
"""
由此当我们查询一条记录的时候,只有满足以下两个条件的记录才会被显示出来:
1.当前事务id要大于或者等于当前行的create_version值,这表示在事务开始前这行数据已经存在了。
2.当前事务id要小于delete_version值,这表示在事务开始之后这行记录才被删除。
"""
存储过程
可以看成是python中的自定义函数
# 无参函数
delimiter $$
create procedure p1()
begin
select * from cmd;
end $$
delimiter;
# 调用
call p1()
# 有参函数
delimiter $$
create procedure p2(
in m int, # in表示这个参数必须只能是传入不能被返回出去
in n int,
out res int # out表示这个参数可以被返回出去,还有一个inout表示即可以传入也可以被返回出去
)
begin
select * from cmd where id > m and id < n;
set res=0; # 用来标志存储过程是否执行
end $$
delimiter;
# 针对res需要先提前定义
set @res=10; 定义
select @res; 查看
call p2(1,5,@res) 调用
select @res 查看
"""
查看存储过程具体信息
show create procedure pro1;
查看所有存储过程
show procedure status;
删除存储过程
drop procedure pro1;
"""
# 大前提:存储过程在哪个库下面创建的只能在对应的库下面才能使用!!
# 1.直接在mysql中调用
set @res=10 # res的值是用来判断存储过程是否被执行成功的依据,所以需要先定义一个变量@res存储10
call p1(2,4,10); # 报错
call p1(2,4,@res);
# 查看结果
select @res; # 执行成功 @res变量值发生了变化
# 2.在python程序中调用
pymysql链接mysql
产生的游标cursor.callproc('p1',(2,4,10)) # 内部原理:
@_p1_0=2,@_p1_1=4,@_p1_2=10;
cursor.excute('select @_p1_2;')
函数
可以看成是python中内置函数
"ps:可以通过help 函数名 查看帮助信息!"
# 1.移除指定字符
Trim、LTrim、RTrim
# 2.大小写转换
Lower、Upper
# 3.获取左右起始指定个数字符
Left、Right
# 4.返回读音相似值(对英文效果)
Soundex
"""
eg:客户表中有一个顾客登记的用户名为J.Lee
但如果这是输入错误真名其实叫J.Lie,可以使用soundex匹配发音类似的
where Soundex(name)=Sounex('J.Lie')
"""
# 5.日期格式:date_format
'''在MySQL中表示时间格式尽量采用2022-11-11形式'''
create table blog (
id int primary key auto_increment,
name char (32),
sub_time detetime
);
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 data_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');
1.where Date(sub_time) = '2015-03-01'
2.where Year(sub_time) = 2016 and Month(sub_time) = 07;
# 更多日期处理相关函数
adddate 增加一个日期
addtime 增加一个时间
datediff 计算两个日期差值
流程控制
# 分支结构
declare i int defaule 0;
if i = 1 then
select 1;
elseif i = 2 then
select = 2;
else
select 7;
end if;
# 循环结构
declare num int;
set num = 0;
while num < 10 do
select num ;
set num = num + 1 ;
end while;
索引相关概念
1.索引就好比一本书的目录,它能让你更快的找到自己想要的内容
2.让获取的数据更有目的性,从而提高数据库检索数据的性能
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构
* primary key
* unique key
* index key
1.上述的三个key都可以加快数据查询
2.primary key和unique key除了可以加快查询本身还自带限制条件而index key很单一就是用来加快数据查询
3.外键不属于索引键的范围 是用来建立关系的 与加快查询无关
索引加快查询的本质
id int primary key auto_increment,
name varchar(32) unique,
province varchar(32)
age int
phone bigint
select name from userinfo where phone=18818888888; # 一页页的翻
select name from userinfo where id=99999; # 按照目录确定页数找
索引可以加快数据查询 但是会降低增删的速度
通常轻快下 我们频繁使用某些字段查询数据
为了提升查询的速度可以将该字段建立索引
聚集索引(primary key)
主键、主键索引
辅助索引(unique,index)
除主键意外的都是辅助索引)
全⽂索引(FULLTEXT)
覆盖索引
select name from user where name='jason';
非覆盖索引
select age from user where name='jason';
索引数据结构
索引底层其实是树结构>>>:树是计算机底层的数据结构
树有很多中类型
二叉树、b树、b+树、B*树......
二叉树
二叉树里面还可以细分成很多领域 我们简单的了解即可
二叉意味着每个节点最大只能分两个子节点
B树
所有的节点都可以存放完整的数据
B+\*树
只有叶子节点才会存放真正的数据 其他节点只存放索引数据
B+叶子节点增加了指向其他叶子节点的指针
B*叶子节点和枝节点都有指向其他节点的指针
辅助索引在查询数据的时候最后还是需要借助于聚集索引
辅助索引叶子节点存放的是数据的主键值
有时候就算采用索引字段查询数据 也可能不会走索引!!!
最好能记三个左右的特殊情况
"""
1.索引字段使用函数
eg:select * from t where month(t) = 7;
month(t)破坏了索引字段在索引树上的有序性,索引之所以快,就是因为索引在各层上是有序的
2.模糊查询like 以%开头
eg:explain select * from user where name like '%张';
3.or前后没有同时使用索引
eg:explain select * from user where name='jason' or age=18;
虽然name字段上加了索引,但是age字段没有索引,使用or的时候会全表扫描
4.在索引字段进行计算操作
eg:explain select * from user where id+1=2;
5.隐式字段类型转换
如果所有字段name是varchar类型,如下sql不走索引
eg:select * from t where name = 1
因为在mysql中,字符串和数字比较时,字符串会编程数字再比较
上面的sql语句相当于
eg:select * from t where cast(name as integer)=1
"""
慢查询优化
explain
1.idenx 尽量避免
2.range
3.ref
4.eq_ref
5.const
6.system
7.null