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格式 对象
3.数据库服务(重点)
统一路径 统一操作方式
降低学习成本 提高开发效率
数据库软件应用史
1.单机游戏
数据储存于各个计算机的本地 无法共享
2.网络游戏
数据储存于网络中 可以共享(数据库服务)
数据库服务集群:提升数据的安全性
数据库的本质
1.站在底层原理的角度
数据库指的是操作数据的进程(一堆代码)
2.站在实际应用的角度
数据库指的是可视化操作界面(一些软件)
ps:以后不做特殊说明的情况下讲数据库其实指的是数据库全歼
数据库全歼本质也是CS架构的程序
意味着所由的程序员其实都有资格编写一款数据库软件
数据库的分类
1.关系型数据库
特征1:拥有固定的表结构(字段名 字段类型)
id name pwd
特征2:数据之间可以建立数据库层面关系
用户表数据
豪车表数据
豪宅表数据
MySQL 、Oracle、MariaDB、PostgreSQL、sql server、sqlite、db2、access
1.MySQL:开源免费 使用最广 性价比贼高
2.Oracle:收费 使用成本高安全性也是最高
3.PostgreDB:开源免费 支持二次开发 兼容性高
4.MariaDB:跟MySQL是一个作者 开源免费
5.sqlite:小型数据库 主要用于本地测试
2.非关系型数据库
特征1:没有固定的表结构 数据存储采用k:v键值对形式
{'name':'jason}
{'username':'jason','password':123}
特征2:数据之间无法建立数据库层面关系
可以自己编写代码建立逻辑层面的关系
redis、mongoDB、memcache
1.redis:目前最火 使用频率最高的非关系型数据库(缓存数据库)
虽然缓存数据库是基于内存做数据存取但是拥有持久化的功能
2.mongoDB:文档型数据库 最像关系型数据库的非关系型数据库
主要用在爬虫以及大数据领域
3.memcache:已经被redis淘汰
MySQL简介
1.版本问题
8.0:最新版
5.7:使用频率最高
5.6:学习推荐使用
ps:站在开发的角度使用哪个版本学习都没有关系
2.下载流程
1.访问官网
2.点击DOWNLOADS并点击GPL
3.点击community server
4.点击Archives
5.选择对应系统的对应版本下载即可(zip压缩包)
MySQL基本使用
cmd建议你是用管理员身份打开
1.切换到mysql的bin目录下先启动服务端
mysql
2.保持窗口不关闭 重新打开一个cmd窗口
3.切换mysql的bin目录下启动客户端
mysql
"""
直接使用mysql命令默认是游客模式 权限和功能都很少
mysql -u用户名 -p密码
管理员默认没有密码 连续回车即可
mysql -uroot -p
"""
'''
有些同学的电脑在启动服务端的时候就会报错 不要慌
拷贝报错信息 然后百度搜索
mysql启动报错粘贴错误信息
系统服务的制作
1.先把bin目录添加到环境变量
清空之前打开的cmd窗口 一定要把之前用cmd启动的服务端关掉(ctrl+c)
2.将mysql添加到系统服务中
1.如何查看系统服务
鼠标右键任务栏选择服务
cmd输入services.msc回车
2.以管理员身份打开cmd
mysql --install
3.首次添加不会自动启动 需要人为i操作一下
1.鼠标右键点击启动
2.命令行启动
net start mysql
如果想要卸载重新安装
1.先关闭服务端
net stop mysql
2.移除系统服务
mysql --remove
密码相关操作
1.修改密码
方式1:mysqladmin
mysqladmin -uroot -p原密码 password 新密码
方式2:直接修改存储用户数据的表
方式3:冷门操作 有些版本可能还不支持
set password=password('新密码')
2.忘记密码
方式1:卸载重新安装
方式2:把data目录删掉 拷贝大雄的目录
方式3:小把戏操作
1.关闭正常的服务端
2.以跳过授权表的方式重启服务端(不校验密码)
3.以管理员身份进入然后修改mysql.user表数据即可
net stop mysql
mysql --skip-grant-table
mysql -uroot -p
update mysql.user set password=password('新密码') where Host='localhost' and User='root';
4.关闭服务端 然后以正常方式启动就就可以
SQL与NoSQL
数据库服务端是可以读物多种类型的客服端
客服端可以是自己开发的 也可以是python代码编写 也可以是java代码编写
SQL
操作关系型数据库的语言
NoSQL
操作非关系型数据库的语言
ps:要想跟数据库交互就必须使用数据库指定的语言
SQL有时候也指代关系型数据库
NoSQL有时候也只带非关系型数据库
数据库重要概念
强调:小白阶段为了更加方便的理解 做了一下比喻 本质其实有一点点的区别
库 相当于是 文件夹
表 相当于是 文件夹里的文件
记录 相当于是 文件夹里的文件中的一行行数据
验证
1.查看所有库的名称
show databases;
2.查看所有表名称
show tables;
3.查看所有的记录
select * from mysql.user;
基本SQL语句
1.sql语句必须以分号结尾
2.sql语句编写错误之后不用担心 可以直接执行报错就可以
基于库的增删改查
1.创建库
create databases 库名;
2.查看库
show databases; 查看所有库名称
show creat datase 库名; 查看指定库信息
3.编辑库
alter database 库名 charset='utf8';
4.删除库
drop database 库名
基于表的增删改查
操作表之前需要先确定库
create database lzy;
切换操作库
use lzy;
1.创建表
create table 表名(字段名 字段类型,字段名 字段类型);
2.查看表
show tables; 查看库下所有的表名称
show create table 表明;查看指定表信息
describe 表名; 查看表结构
decs 表名;
ps:如果想跨库操作其他表 只需要在表名前加库名即可
desc mysql.user;
3.编辑表
alter table 表名 rename 新表名;
4.删除表
drop table 表名;
基于记录的增删改查
1.插入数据
insret into 表名 values(数据值1,数据值2);
2.查询数据
select * from 表名; 查询表中的所有数据
3.编辑数据
update 表名 set 字段名=新数据 where 筛选条件
4.删除数据
delete from 表名; 删除表中所有数据
delete from 表名 where id=111;删除指定数据
今日内容详细
字符编码与配置文件
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
修改了配置文件中关于[mysql]的配置 需要重启服务端
利用配置文件我们可以偷懒
将管理员登录的账号密码直接卸载配置文件中 之后使用mysql登录即可
[mysql]
user = 'root'
password=123
数据库存储引擎
存储引擎
数据库针对数据采取的多种存取方式
查看常见存储引擎的方式
show engines;
需要了解四个储存引擎
MYISAM
mysql 5.5之前默认的储存引擎
存取数据的速度快 但是功能少 安全性能低
InnoDB
mysql 5.5之后默认的存储引擎
支持事务、行锁、外键等 存取速度没有MYISAM快 但是安全性能高
Memory
基于内存存取数据 仅用于临时表数据存取
BlackHole
任何写进去的数据都会立刻丢失
了解不同存储引擎底层文件个数
先创先一个库
create database lzy;
进入库
use lzy2;
create table t1(id int) engine=innodb;
create table t2(id int) engine=myasam;
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);
ps:mysql默认忽略大小写
创建表的完整语法
create table 表名(
字段名 字段名类型(数字)约束条件,
字段名 字段名类型(数字)约束条件,
字段名 字段名类型(数字)约束条件
);
1.字段名和字段类型是必须需要有的
2.数字和约束条件是可选的
3.约束条件也可以写多个 空格隔开即可
4.最后一行结尾不能加逗号
ps:编写sql语句报错之后不要慌 仔细查看提示 会很快解决
字段类型之整型
tinyint 1bytes 正负号(占1bit)
smallint 2bytes 正负号(占1bit)
int 4bytes 正负号(占1bit)
bigint 8bytes 正负号(占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 globle 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.1111111111111111111111111111);
insert into t8 values(1.1111111111111111111111111111);
insert into t9 valuse(1.1111111111111111111111111111);
三者的核心区别在于精准度不同
float < double < decimal
字段类型之字符类型
char
定长
char(4) 最多储存4个字符 超出就报错 不够四个空格填充至4个
varchar
变长
varchar(4)最多存储4个字符 超出就报错 不够则有几位存几位
create table t10(id int,name char(4));
create table t11(id int,name varchar(4));
insert into t10 values(1,'jason1');
insert into t11 values(2,'jason2');
ps:char_length()获取字段存储的数据长度
默认情况下mysql针对char的存储会自动填充空格和删除空格
set global sql_mode='strict_trans_tables,pad_char_to_full_length';
char VS varchar
char
优势:整存整取 速度快
劣势:浪费存储空间
varchar
优势:节省存储空间
劣势:存取数据的速度教char慢
jacktonyjasonkevintomjerry
1bytes+jack1bytes+tony1bytes+jason1bytes+kevin1bytes+tom1bytes+jerry
"""
char与varchar的使用需要结合具体应用场景
"""
数字的含义
数字在很多地方都用来表示限制存储数据的长度
但在整型中数字却不是用来限制存储长度
create table t12(id int(3)); 不是用来限制长度
insert into t12 values(123456);
cerate table t13(id int(5));
insert into t13 values(123,(123456789));
create table t14(id int)
'''写整型不用添加数字'''
字段类型之枚举与集合
枚举
多选一
create table t15(
id int,
name varchar(32),
gender enum('male','female','others')
);
inster into t15 values(1,'tony','猛男');
inster into t15 values(2,'jason','male');
insert into t15 values(3,'kevin','other');
集合
多选多(多选一)
create table t16(
id int,
name varchar(16),
hobbies set('read','music','paint')
);
insert into t16 values(1,'jason','studay');
insert into t16 values(2,'kevin','1234645135');
insert into t16 values(3,'tony','music,paint')
字段类型之日期类型
datetime 年月日时分秒
date 年月日
time 时分秒
year 年
create table t17(
id int,
name varchar(32),
register_time datetime,
birthday daye,
study_time time,
work_time year
);
insert into t17 values(1,'jason','2011-2-2 22:22:22','2012-02-20','20:20:20','2021');
ps:以后涉及到日期相关字段一般都是系统自动获取 无需我们自己操作
无符号、零填充
unsigned
id int unsigned
查看表结构 显示int unsigned 没有符号
插入小数 查看表 只有整数位 小数点后面的自动省略
zerofill
id int(5)zerofill
查看表结构 显示int(5)zerofill 5位数 不够位数用0填充
非空
create table t3(
id int,
name varchar(32)
);
查看表结构 null一栏都是yes 说明数据都可以为空
insert into t3 values(1);
insert into t3 values('jason');
insert into t3 values(2,'kevin');
ps:所有字段类型不加约束条件的情况下默认都可以为空
create table t4(
id int,
name varchar(32)not null
);
在name字段类型后面加not null 不能为空 表结构中null一栏里面编成了no
insert into t4(id)values(1);
insert into t4(name)values('jason');
insert into t4 values(2,'kevin');
insert intp t4 values(2,'');
insert into t4 values(2,null);
name 不能为为空
当只插入id时 报错
只插入name时id可以插也可以为空
name字段也可以插入空的字符
默认值
create table t6(
id int default 666,
name varchar(32) default '你好',
);
查看表结构 default一列中显示字段对应的默认值
insert into t6(id) values(1);
insert into t6(name) values('jason');
insert intp t6 values(1,'jason');
只插入id时name显示默认字段
只插入name时id显示默认字段
唯一值
'''单列唯一'''
create table t7(
id int,
name varchar(32)unique
);
查看表结构中 name的key 一列显示uni
insert into t7(name) values('jason');
name 的数值是唯一的不能有重复的
'''联合唯一'''
create table t8(
id int,
ip vachar(32),
port int,
unique(ip,port)
);
将想要指定的多个字段放在最下面 写在一起 单独写一个unique字段
insert into t8 values(1,'127.0.10',8080),(2,'127.0.10',8080);
指定ip和port为唯一值 重复添加就会报错
主键
1.单从约束层面上而言主键相当于not null+unique(非空且唯一)
create table t9(
id int primary key,
name varchar(32)
);
查看表结构 key一栏变成pri
insert into t10 values(1,'jason');
insert into t10 values(1,'kevin');
insert into t10 values(2,'jason');
id为主键时 不能是空的而且必须只能有一个
2.innoDB存储引擎规定了所有的表都必须有且只有一个主键
主键是组织数据的重要条件并且主键可以加快数据的查询速度
1.当表中没有主键也没有其他非空且唯一的之端的情况下
innoDB 会采用一个隐藏的字段作为表的主键 隐藏意味着无法使用 基于该表的数据查询只能一行行查找 速度很慢
2.当表中没有主键但是有其他非空且唯一的字段 那么会从上往下将第一个该字段自动升级为主键
create table t11(
id int,
age int not null unique,
birth int not null unique
);
'''
当我们在创建表的时候应该有一个字段用来标识数据的唯一性 并且该字段通常情况下就是‘id’字段
id nid sid
create table t11(
id int primary key,
);
'''
自增
该约束条件不能单独出现 并且一张表中只能出现一次 主要就是配合主键一起用
create table t12(
id int primary key,
name varchar(32)
);
create table t13(
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 dep(
id int primary key auto_increment,
dep_name varchar(32),
dep_desc varchar(32)
);
create table emp(
id int primary key auto_increment,
name varchar(32),
age int
);
1.创建表的时候一定要先创建被关联的表
2.录入表数据的时候一定要先录入被关联表
3.修改数据的时候外键字段无法修改和删除
'''针对3有措施>>>:级联更新 级联删除'''
create table emp1(
id int primary key auto_increment,
name varchar(32),
age int
);
create table dep1(
id int primary auto_increment.
dep_name varchar(32),
dep_desc varchar(32)
);
'''
外键其实就是强耦合 不符合解耦合的特性
所以很多时候 实际项目中当表较多的情况 我们可能不会使用外键 而是使用代码建立逻辑层面的关系
'''
多对多关系
以书籍表和作者表为例:
1.先站在书籍表的角度
问:一本书能否对应多个作者
答:可以
2.再站在作者的角度
问:一个作者能否对应多本书
答:可以
结论:两个都可以 关系就是‘多对多’
针对‘多对多’不能在表中直接创建 需要新建第三张关系表
create table book(
id int primary auto_increment,
name varchar(32),
price float(5,2)
)
create table author(
id int primary auto_increment,
name varchar(32),
phone bigint
)
create table book2outhor(
id int primary auto_increment,
author_id int,
foreign key(author_id)references author(id)
on update cascade
on delete cascade,
book int,
foreign key(book_id)references book(id)
on update cascade
on delete cascade
);
一对多关系
以用户表和用户详情表为例:
1.先站在用户表的角度
问:一个用户能否对应多歌用户详情
答:不可以
2.再站在用户详情表的角度
问:一个用户详情能否对用多个用户
答:不可以
结论:两个都可以 关系就是‘一对一’或者没有关系
针对‘一对一外键字段在任何一放都可以 但是推荐建在查询频率较高的表中
create table user(
id int primary auto_increment,
name varchar(32),
detail_id key(detail_id)references userdetail(id)
on update cascade
on delete cascade
);
create table userdetail(
id int primary auto_increment,
phone bigint
);
SQL语句查询关键字
select
指定需要查询的字段信息
select * 查所有字段
select name 查所有name字段
select char_length(name) 支持对字段做处理
from
指定需要查询的表信息
from mysql.user
from t1
SQL语句中关键字的执行顺序喝编写顺序并不一样 可能会导致错乱
eg:
select id,name from userinfo;
我们先写的select再写from 但是执行的时候是先执行的from再执行select
对应关键字的编写顺序和执行顺序我们没必要过多的在意 熟练之后会非常自然的编写 我们只需要把注意力放在每个关键字的功能上即可
前期数据准备
create table emp(
id int primary key auto_increment,
name varchar(32),
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,dire_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后面很少直接写* 因为*代表所有 当表中字段和数据都特别多的情况下非常的浪费数据库资源
查询关键字之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 只要有o就能查询到
%o jdisho idhho 匹配右侧有o的字段
o% ofjh oooo 匹配左边有o的字段
_:匹配单个个数的任意字符
eg:
_o_ ioh fol
_o io po
o_ ol op
5.查询员工姓名是由四个字符组成的员工姓名与其薪资
select * from emp where name like '____';
select * from emp where char_length(name)=4;
6.查询岗位描述为空的员工名与岗位名 针对null不能用等号,只能用is
select * from emp where post_comment is NULL;
在MySQL中有很多内置方法 我们可以通过查看帮助手册学习
help 方法名
查询关键字之group by分组
分组:按照指定的条件将单个单个的数据组成一个整体
eg:
将班级同学按照性别分组
将全国人民按照民族分组
将全世界人按照肤色分组
分组的目的是为了更好的统一相关数据
eg:
每个班级的男女比例
每个民族人民的占比
每个部门的平均薪资
聚合函数
专门用于分组之后的数据统计
max\min\sum\avg\count
最大值、最小值、求和、平均值、计数
1.将员工数据按照部门分组
select post from emp group by post;
select * from emp group by post;
mysql 5.6默认不会报错
set global sql_mode='strict_trans_tables,only_full_group_by'
mysql 5.7及8.0默认都会报错
原因是分组之后 select后面默认只能直接填写分组的依据 不能再写其他字段
select post from emp group by post;
select age from emp group by age;
分组之后默认的最小单位应该是组 而不应该再是组内的单个数据单个字段
2.获取每个部门的最高工资
要不要分组我们完全可以从题目中分c析出来 尤其是出现关键字 每个 平均
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 poost;
5.统计每个部门的部门名称以及部门下的员工姓名
分组以外的字段无法直接填写 需要借助于方法
select post,name from emp group by post;
select post,name 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)>1000;
查询关键字之distinct去重
去重有一个必须的条件也是很容易被忽略的条件 >>>:数据必须一摸一样才可以去重
select distinct age from emp;
select distinct post,age from emp;
查询关键字之order by排序
1.可以是单个字段排序
select * from emp order by age; 默认升序
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(salsry) from emp where age >10 group by post;
3.针对分组的结果做二次筛选
select post,avg(salsry) from emp where age >10 group by post having avg(salsry) >1000;
4.最后按照指定字段排序
select post,avg(salsry) from emp where age >10 group by post having avg(salsry) >1000 order by avg(salsry) ;
查询关键字之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 emp(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;
基于上述的操作就可以将多张表合并到一起然后一次性获取更多的数据
多种表查询的两种方式
方式1:连表操作
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填充
'''
连接多张表思路
先连接两张表 将连接好的表起一个别名再去和下一张表拼接 以此往复
'''
方式2:子查询
将一条SQL语句用括号括起来当成另外一条SQL语句的查询条件
题目:求姓名是jason的员工部门名称
子查询类似于我们日常生活中解决问题的方式>>>:分步操作
步骤1:先根据jason获取部门编号
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 与 cancat_ws
cancat 用于分组之前的字段拼接操作
select concat(name,|,sex)from emp;
concat_ws
select concat_ws('|',name,sex)from emp;
2.exists
sql1 exists sql2
sql2有结果的情况下才会执行sql1 否则不执行qsl1 返回空数据
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文件
多表查询练习题
'''
1.先确定需要用到几张表
2.预览表中的数据
3.确定多表查询的思路
4.确定最终需要几张表 用连表还是子代码查询
'''
python操作MySQL
pymasql模块
import pymysql
#1.连接mysql服务端
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123',
db='lzy5',
charset='utf8mb4'
)
#2.产生游标对象
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
#3.编写sql语句
sql = 'select * from teacher;'
#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(0,'absolute') #基于数据的开头往后移动
2.增删改查
autocommit = True #针对增删改查自动确认(直接配置)
conn.commit() #针对增删改查需要2次确认(代码确认)
今日内容
SQL注入问题
怪像1:输入用户名就可以登录成功
怪像2:不需要对的用户名和密码也可以登陆成功
sql注入:利用特殊符号的组合产生特殊的含义 从而避开正常的业务逻辑
select * from userinfo where name='jason' --siahdub
select * from userinfo where name='jason' or 1=1 --sajhduiog
针对上述的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_insert_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';
update user set balance=1010 where name='kevin';
update user set balance=1090 where name='tank';
#回滚到上一个状态
rollback;#未修改之前
#开启事务之后 只要没有执行commit操作 数据其实都是没有真正刷新到硬盘
commit;
'''
事物相关关键字
start transaction;
rollback
commit
sacepoint
'''
在sql标准中定义了四种隔离级别 每一种级别都规定了一个事务中所做的修改
innoDB支持所有隔离级别
set transaction isolation level 级别
1.read uncommitted (未提交续)
事务中的修改即使没有提交 对其他事物也都是可见的 事物可以读取未提交的数据 这一下想也称之为‘脏读’
2.read committed(提交读)
大多数数据库系统默认的隔离级别
一个事务从开始直到提交之前所做的任何修改对其他事务都是不可见的 这种级别也叫做‘不可重复读’
3.repeatable read(可重复读)
能够解决‘脏读’问题 但是无法解决‘幻读’
所谓幻读指的是当某个事物在读取某个范围内的记录时另外一个事物又在该范围内插入了新的记录 当之前的事物再次读取该范围的记录会产生幻行 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 p1(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)=Soundex('J.Lie')
'''
#5.日期格式
date_format
'''在mysql中标识时间格式尽量采用2022-11-29形式'''
CREATE TABLE blog (
id INT PRIMARY KEY auto_increment,
NAME CHAR (32),
sub_time datetime
);
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 date_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 default 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 key)
除了主键以外的都是辅助索引
覆盖索引
select name from user where name='jason';
非覆盖索引
select age from user where name ='jason';
索引数据结构
索引底层其实是树结构>>>:树是计算机底层的数据结构
树有很多种类
二叉树 b树 b+树 b*树..
二叉树
二叉树里面还可以细分成很多领域 我们可以简单的了解
二叉意味着每个节点最大只能分两个子节点
b树
所有节点都可以存放完整的数据
b+/b*树
只有叶子节点才会存放真正的数据 其他节点只存放索引数据
b+叶子节点增加了指向奇特叶子节点的指针
b*叶子节点和枝节点都有指向其他节点的指针
辅助索引在查询数据的时候最后还是需要借助于聚集索引
辅助索引叶子节点存放的是数据的主键值
有时候就算采用索引字段查询数据 也可能不会走索引!
慢查询优化
explain
1.index 尽量避免
2.range
3.ref
4.eq_ref
5.const
6.system
7.null
测试索引
准备
#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_insert1\G
#4. 调用存储过程
call auto_insert1();
# 表没有任何索引的情况下
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字段一剑封喉
联合索引
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'; # 速度变快
全文检索
mysql 的全文检索功能myisam存储引擎支持而innoDB存储引擎不支持
一般在创建表的时候启用全文检索功能
create table t1(
id int primary key auto_increment,
content text
fulltext(content)
)engine=MyISAM;
#match括号内的值必须是fulltext括号中定义的(单个或者多个)
select content from t1 where match(content) against('jason')
'''上述语句可以用like实现但是查询出来的结果顺序不同 全文检索会以文本匹配的良好程度排序数据在返回效果更佳'''
#查询扩展
select note_text from productnotes where Math(note_text) Against('jason' with query expansion);
"""
返回除jason外以及其他jason所在行相关文本内容行数据
eg:
jason is handsome and cool,every one want to be cool,tony want to be more handsome;
二三句虽然没有jason关键字 但是含有jason所在行的cool和handsome
"""
# 布尔文本搜索
即使没有定义fulltext也可以使用,但是这种方式非常缓慢性能低下
select note_text from productnotes where Match(note_text) Against('jason' in boolean mode);
# 注意事项
1.三个及三个以下字符的词视为短词,全文检索直接忽略且从索引中排除
2.MySQL自身自带一个非用词列表,表内词默认均被忽略(可以修改该列表)
3.出现频率高于50%的词自动作为非用词忽略,该规则不适用于布尔搜索
4.针对待搜索的文本内容不能少于三行,否则检索不返回任何结果
5.单引号默认忽略
插入数据
数据库经常被多个用户访问,insert操作可能会很耗时(特别是有很多索引需要更新的时候)而且还可能降低等待处理的select语句性能
如果数据检索是最重要的(一般都是),则可以通过在insert与into之间添加关键字low_priority指示MySQL降低insert语句优先级
insert low_priority into
insert还可以将一条select语句的结果插入表中即数据导入:insert select
eg:想从custnew表中合并数据到customers表中
insert into customers(contact,email) select contact,email from custnew;
更新数据
如果使用update语句更新多列值,并且在更新这些列中的一列或者多列出现一个错误会导致整个update操作被取消,如果想发生错误也能继续执行没有错误的更新操作可以采用
update ignore custmoers ...
"""
update ignore set name='jason1',id='a' where id=1;
name字段正常修改
update set name='jason2',id='h' where id=1;
全部更新失败
"""
删除数据
delete语句从表中删除数据,甚至可以是所有数据但是不会删除表本身
并且如果想从表中删除所有的行不要使用delete可以使用truncate速度更快并且会重置主键值(实际是删除原来的表并重新创建一个表而不是逐行删除表中的数据)
主键
查看当前表主键自增到的值(表当前主键值减一)
select last_insert_id();
外键
MySQL存储引擎可以混用,但是外键不能跨引擎即使用一个引擎的表不能引用具有使用不同引擎表的外键
重命名表
rename关键字可以修改一个或者多个表名
rename table customer1 to customer2;
rename table back_cust to b_cust,
back_cust1 to b_cust1,
back_cust2 to b_cust2;
事务
MySQL提供两种事务型存储引擎InnoDB和NDB cluster及第三方XtraDB、PBXT
事务处理中有几个关键词汇会反复出现
事务(transaction)
回退(rollback)
提交(commit)
保留点(savepoint)
为了支持回退部分事务处理,必须能在事务处理块中合适的位置放置占位符,这样如果需要回退可以回退到某个占位符(保留点)
创建占位符可以使用savepoint
savepoint sp01;
回退到占位符地址
rollback to sp01;
# 保留点在执行rollback或者commit之后自动释放
安全管理
1.创建用户
create user 用户名 identified by '密码';
"""修改密码"""
set password for 用户名 = Password('新密码');
set password = Password('新密码'); # 针对当前登录用户
2.重命名
rename user 新用户名 to 旧用户名;
3.删除用户
drop user 用户名;
4.查看用户访问权限
show grants for 用户名;
5.授予访问权限
grant select on db1.* to 用户名;
# 授予用户对db1数据库下所有表使用select权限
6.撤销权限
revoke select on db1.* from 用户名;
"""
整个服务器
grant all/revoke all
整个数据库
on db.*
特定的表
on db.t1
"""
隔离级别
在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改
InnoDB支持所有隔离级别
set transaction isolation level 级别
1.read uncommitted(未提交读)
事务中的修改即使没有提交,对其他事务也都是可见的,事务可以读取未提交的数据,这一现象也称之为"脏读"
2.read committed(提交读)
大多数数据库系统默认的隔离级别
一个事务从开始直到提交之前所作的任何修改对其他事务都是不可见的,这种级别也叫做"不可重复读"
3.repeatable read(可重复读) # MySQL默认隔离级别
能够解决"脏读"问题,但是无法解决"幻读"
所谓幻读指的是当某个事务在读取某个范围内的记录时另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录会产生幻行,InnoDB和XtraDB通过多版本并发控制(MVCC)及间隙锁策略解决该问题
4.serializable(可串行读)
强制事务串行执行,很少使用该级别
锁
读锁(共享锁)
多个用户同一时刻可以同时读取同一个资源互不干扰
写锁(排他锁)
一个写锁会阻塞其他的写锁和读锁
死锁
1.多个事务试图以不同的顺序锁定资源时就可能会产生死锁
2.多个事务同时锁定同一个资源时也会产生死锁
# Innodb通过将持有最少行级排他锁的事务回滚
事务日志
事务日志可以帮助提高事务的效率
存储引擎在修改表的数据时只需要修改其内存拷贝再把该修改记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘
事务日志采用的是追加方式因此写日志操作是磁盘上一小块区域内的顺序IO而不像随机IO需要次哦按的多个地方移动磁头所以采用事务日志的方式相对来说要快的多
事务日志持久之后内存中被修改的数据再后台可以慢慢刷回磁盘,目前大多数存储引擎都是这样实现的,通常称之为"预写式日志"修改数据需要写两次磁盘
MVCC多版本控制
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值,这表示在事务开始之后这行记录才被删除。
"""
转换表的引擎
主要有三种方式,并各有优缺点!
# 1.alter table
alter table t1 engine=InnoDB;
"""
适用于任何存储引擎 但是需要执行很长时间 MySQL会按行将数据从原表赋值到一张新的表中,在复制期间可能会消耗系统所有的IO能力,同时原表会加读锁
"""
# 2.导入导出
"""
使用mysqldump工具将数据导出到文件,然后修改文件中相应的SQL语句
1.引擎选项
2.表名
"""
# 3.insert ... select
"""
综合了第一种方案的高效和第二种方案的安全
1.先创建一张新的表
2.利用insert ... select语法导数据
数据量不大这样做非常合适 数据量大可以考虑分批处理 针对每一段数据执行事务提交操作避免产生过多的undo
"""
ps:上述操作可以使用pt-online-schema-change(基于facebook的在线schema变更技术)工具,简单方便的执行上述过程