MySql
数据库的基础知识
数据的存储方式
存储位置 | 优点 | 缺点 |
---|---|---|
内存(集合,实体类对象数据是放在内存中) | 存取速度很快 | 不能永久的保存 |
文件(IO流,把数据存在文件中) | 可以永久操作数据 | 数据的管理和维护不方便 |
数据库 | 可以永久保存数据 数据库管理系统对数据有专门的操作命令,方便我们对数据进行维护和管理 | 数据库通常是要花钱的 |
数据库的概念
- 数据存储的仓库,称为数据库。
- 在计算机中本质上还是一个或多个文件组成
- 由统一的SQL语句来管理和维护数据库, 我们今天要学的就是SQL语句怎么编写,每条SQL语句的作用是什么
数据库的分类
- 关系型数据库:MySQL,Oracle,SQL Server,SQLite,它们有一个共同的特点,都支持SQL语句的操作。如果你会一种关系型数据库的SQL语句,你就可以操作其它所有的数据库。不同的数据库也是有差异的,大同小异。
- 非关系数据库:差异比较大,不同的数据库之间操作几乎都不相同,做为关系型数据库有益的补充。
DBMS:DataBase Manager System 数据库管理系统
Relational DBMS:关系型数据库
常见的数据库
MySQL:开源免费的数据库,中型的数据库。MySQL6.x版本也开始收费。最早是MySQL开发,后来Sun公司收购了MySQL,而Sun公司又被Oracle收购。
Oracle:收费的大型数据库,Oracle公司的产品。
数据库管理系统、数据库和表的关系
什么是SQL
SQL:Structured Query Language 结构化查询语言,它是一种在各个关系型数据库中通用的语言,但不同的关系型数据库也有不同的语法结构。
SQL语句分类
-
Data Definition Language
简称为DDL,数据定义语言,主要用于数据库和表的管理
-
Data Manipulation Language
简称为DML,数据操纵语言,主要用于表的增删改操作
-
Data Query Language
简称为DQL,数据查询语言,主要用于表的查询操作
-
Data Control Language
简称为DCL,数据控制语言,主要用于用户权限的管理
MySQL的语法
- 结尾:每条SQL语句必须以分号结束,但在SQLyog中可以不写,可以选中一部分代码执行,建议都加上分号。
- 大小写: MySQL不区分大小写,关键字中select和SELECT是一样的。
- 在MySQL中注释有以下三种写法:
注释的语法 | 说明 |
---|---|
–空格 | 单行注释 |
/* */ | 多行注释,与Java相同 |
# | 单行注释,不建议,这种是mysql特有,不是通用的方式。 |
MySQL数据类型
常使用的数据类型如下
数据类型 | 关键字 |
---|---|
整型 | int或integer |
浮点型 | double, float decimal(5,2) 整个小数长5位,小数位占2位 |
字符串型 | char定长:char(2) 最长存储2个字符,无论有没有使用2个,都是占用2个。 varchar可变长:如:varchar(100),最长可以保存100个字符 如果只使用了3个,占3个字符的空间。 |
日期类型 | date或time,datetime |
常用数据类型说明
/*
创建表的语法:
create table 表名(
字段名 字段类型 约束,
字段名 字段类型 约束
)
*/
-- 创建数据库
create database day18;
-- 创建student表包含id整数,name变长字符串长20,sex性别定长型1,birthday字段日期类型
create table student(
id int, -- 编号
name varchar(20), -- 姓名
sex char(1), -- 性别
birthday date -- 生日
);
DDL:查看表结构
-- 查看某个数据库中的所有表
show tables;
use mysql;
show tables;
-- 查看表结构,表结构创建好,表中没有任何数据,这是一个空表
use day18;
desc student;
-- 查看创建表的SQL语句,看到的是mysql生成的语句,并不是我们写的,功能是一样的。
show create table student;
-- 创建s1表,s1表结构和student表结构相同。复制表结构,没有其中记录
-- 语法:create table 新表 like 旧表
create table s1 like student;
-- 查看s1表的结构
desc s1;
逻辑运算符
逻辑运算符 | 说明 |
---|---|
and 或 && | 全真为真 |
or 或 || | 见真为真 |
not 或 ! |
关键字
查询关键字 | 作用 |
---|---|
AS | 给列或表定义别名,可以省略 |
DISTINCT | 去重复的值 |
<> | 不等于 |
AND/OR/NOT | 与,或,非 |
BETWEEN…AND | 在一个指定的范围之内,包头包尾 |
IN(多个值) | 多个值匹配其中任何一个 |
LIKE ‘张%’ | 模糊查询,两个通配符:%, _ |
IS NULL | 比较是否是空 |
in关键字
语法
select 列名 from 表名 where 列名 in(值1,值2…) |
---|
in里面的每个数据都会作为一次匹配条件,只要满足条件的就会显示 |
操作
-- 使用in关键字查询效率更高,如果使用or不会使用索引,in可以使用索引
select * from student3 where id in (1,3,5);
范围查询
select 列名 from 表名 where 列名 between 小值 and 大值 |
---|
表示从值1到值2范围,包头又包尾。小值必须放在前面,大值必须放在后面 |
操作
-- 查询english成绩大于等于77,且小于等于87的学生
select * from student3 where english >=77 and english<=87;
-- 可以使用between关键字实现同样的功能
select * from student3 where english between 77 and 87;
like关键字
作用:用于字符串的模糊查询 |
---|
语法:select 列名 from 表名 where 列名 like 关键字 |
如果关键字没有使用通配符,功能与等于相同 |
MySQL通配符
通配符 | 说明 |
---|---|
% | 匹配0~n个字符 |
_ | 匹配一个字符 |
操作
-- 查询名字是马的同学,查询不到记录的,如果要模糊查询必须使用通配符
select * from student3 where name like '马'
-- 查询姓马的学生
select * from student3 where name like '马%'
-- 查询姓名中包含'德'字的学生
select * from student3 where name like '%德%'
-- 查询姓马,且姓名有2个字的学生
select * from student3 where name like '马%' -- 所有姓马的
select * from student3 where name like '马_' -- 只匹配1个字符
select * from student3 where name like '马__' -- 只匹配2个字符
查询为空的列
关键字
IS NULL 查询值是NULL的记录,注:不能写成=NULL
语法:
select 列名 from 表名 where 列名 is null -- 查询指定列的值为空
select 列名 from 表名 where 列名 is not null -- 查询指定列的值为不空
操作
-- 查询英文成绩为NULL的学生
select * from student3;
-- 错误写法:
select * from student3 where english = null;
-- 正确写法:
select * from student3 where english is null;
-- 查询英语成绩不为NULL的学生
-- 错误写法:
select * from student3 where english != null;
-- 正确写法:
select * from student3 where english is not null;
-- 查询姓名和英语成绩,如果英语为null,则显示为0分
select name,english from student3;
-- 函数: ifnull(字段名, 默认值) 如果这一列不为空,就显示它原来的值,如果为空,就显示后面这个默认值
select name,ifnull(english,0) from student3;
相关的函数说明
mysql函数 | 说明 |
---|---|
IFNULL(字段名,默认值) | 如果这一列不为空,就显示它原来的值,如果为空,就显示后面这个默认值 |
order by 按指定的列名进行排序,列名可以指定1列或多列
select 列名 from 表名 where 列名 order by 列名 [asc/desc], 列名 [asc/desc] |
---|
升序: asc 默认可以省略 |
降序: desc |
多列排序
-- 默认是升序,不会影响数据库的物理顺序,只是显示的结果不同
select * from student3 order by age;
-- 查询所有数据,使用年龄降序排序
select * from student3 order by age desc;
-- 查询所有年龄大于20岁的学生,在年龄降序排序的基础上,如果年龄相同再以数学成绩升序排序
select * from student3 where age>20 order by age desc, math asc;
五个聚合函数
SQL中的聚合函数 | 作用 |
---|---|
count(列名) | 求这一列所有的记录数 |
sum(列名) | 求这一列所有值的总和 |
max(列名) | 求这一列中最大的值 |
min(列名) | 求这一列中最小的值 |
avg(列名) | 求这一列所有值的平均值 |
-- 查询学生总数
-- 列名可以是所有列,也可以指定列
select count(id) 总记录数 from student3;
select count(*) 总记录数 from student3;
-- 可以写成0,查询效率比较高
select count(0) 总记录数 from student3;
-- 查询年龄大于40的总数
select count(id) 总记录数 from student3 where age>40;
-- 查询数学成绩总分
select sum(math) 数学总分 from student3;
-- 查询数学成绩平均分
select avg(math) 数学平均分 from student3; -- 不能整除就是小数
-- 查询数学成绩最高分
select max(math) 数学最高分 from student3;
-- 查询数学成绩最低分
select min(math) 数学最低分 from student3;
-- 如果要知道最高分或最低分是谁,目前还做不到,通过子查询来实现
分组查询
select 列名 from 表名 where 条件 group by 列名 having 过滤条件 |
---|
group by 按指定的列进行分组 |
having 指定分组后的过滤条件 |
GROUP BY将分组字段结果中相同内容作为一组,并且返回每组的第一条数据,所以单独分组没什么用处。分组的目的就是为了统计,一般分组会跟聚合函数一起使用。
-- 按性别进行分组,求男生和女生数学的总成绩
select sex, sum(math) 数学总分 from student3 group by sex; -- 同时显示性别这一列
-- 求男生和女生各是多少个人
select sex, count(*) 人数 from student3 group by sex;
-- 求男生和女生数学平均分是多少
select sex, avg(math) 数学平均分 from student3 group by sex;
对分组过后的结果过滤:
-- 查询年龄大于25岁的人,按性别分组,统计每组的人数,并只显示性别人数大于2的数据。
-- You have an error in your SQL syntax; SQL语法错误
SELECT sex, COUNT(*) FROM student3 WHERE age > 25 GROUP BY sex WHERE COUNT(*) >2;
-- having是先分组,再过滤
SELECT sex, COUNT(*) FROM student3 WHERE age > 25 GROUP BY sex having COUNT(*) >2;
having与where的区别
关键字 | 功能 |
---|---|
where子句 | 1. 放在group by的前面 2. 先过滤,再分组 3. 后面不能使用聚合函数 |
having子句 | 1. 放在group by的后面 2. 先分组,再过滤,对分组以后的结果再次进行过滤 3. 后面可以使用聚合函数 |
limit的作用:
作用:中文意思是限制的意思。
语法:select 列名 from 表名 limit 起始索引(从0开始), 返回的行数
select的关键字顺序
select 字段 from 表 查询所有行
where 条件 指定行过滤的条件
group by 分组列 按指定的列进行分组
having 过滤条件 先分组,再过滤
order by 排序列 排序,asc/desc
limit 跳过行, 返回行 分页查询
先后顺序是固定的
约束种类
约束名 | 约束关键字 |
---|---|
主键 | primary key |
非空 | not null |
唯一 | unique |
外键 | foreign key … references |
检查约束 | check 注:mysql不支持 |
修改主键自增的起始值
-- 将主键的起始值设置为1000
alter table st2 auto_increment = 1000;
-- 表中已经存在的记录不会有影响,只会对新插入的记录有影响
insert into st2 values (null, '王五', 22);
外键约束:级联更新和删除
目标
多表中级联更新和删除
为什么要级联操作
-- 要把部门表中的id值2,改成5,能不能直接更新呢?
select * from department;
-- Cannot delete or update a parent row: 不能更新主表中行
update department set id=5 where id=2;
-- 要删除部门id等于1的部门, 能不能直接删除呢?
-- Cannot delete or update a parent row: 不能删除主表中行
delete from department where id=1;
-- 如果有外键约束,主表中主键不能随意更新或删除
什么是级联操作
在更新或删除主键中主键的同时,级联更新或删除从表中外键值
语法
以下语句写在创建外键的后面,就可以实现级联更新或级联删除
级联操作 | 语法 |
---|---|
级联更新 | on update cascade |
级联删除 | on delete cascade |
操作
-- 删除外键约束,通过约束的名字来删除
alter table employee drop foreign key fk_emp_dept;
-- 添加外键约束,级联更新和级联删除,可以2个都写,也可以只写一个
alter table employee add constraint fk_emp_dept foreign key(dep_id) references department(id) on update cascade on delete cascade;
-- 把部门表中id等于2的部门改成id等于5
select * from department;
update department set id=5 where id=2;
select * from employee;
-- 删除部门号是1的部门
delete from department where id=1; -- 不建议使用级联删除,导致整个部门所有的员工都被删除
第一范式
概念
第一范式:表中的每一列都是原子的,不可再拆分成更小的列,看实际的业务需求来定,这一列是否做为一个整体来使用。
第二范式
概念
在满足第一范式的前提下,表中每一列都完全依赖于主键。主要是因为存在复合主键。
复合主键:表中一行有2列以上共同做为主键。
单一主键:表中只一列是主键,单一主键都是符合第二范式。
不存在局部依赖的问题,表中有些列是依赖于复合主键中一列,其它列依赖于复合主键的另一列。一张表只描述一件事情,如果描述了2件事情以上,则不符合第二范式。
第三范式
概念
在满足第二范式的前提下,表中每一列都直接依赖于主键,而不是通过其它列来间接依赖于主键。
依赖关系
所谓传递依赖,指的是如果存在"A → B → C"的决定关系,则C传递依赖于A。
满足第三范式的数据库表应该不存在如下依赖关系:主键列 → 非主键列x → 非主键列y
表连接:笛卡尔积和内连接
表连接的分类
表连接查询:
分为内连接和外连接
内连接:隐式内连接和显式内连接
外连接:左外连接(左连接)和右外连接(右连接)
笛卡尔积现象
什么是笛卡尔积
隐式内连接语法
语法:
select 列名 from 左表 ,右表 where 主表.主键 = 从表.外键; -- as省略
隐藏内连接没有join关键字
显式内连接语法
无论是隐式还是显式,它们的查询结果是一样的,只是语法不同
左边 inner join 右表 on 表连接条件
语法
select 列名 from 左表 inner join 右表 on 主表.主键 = 从表.外键
-- 显式内连接
select * from dept d inner join emp e on d.id = e.dept_id;
左连接
语法
select * from 左表 left join 右表 on 主表.主键=从表.外键
-- 需求:在部门表中增加一个销售部,需要查询所有的部门和员工,将部门表设置成左表,员工表设置成右表
select * from dept;
insert into dept values(null,'销售部');
-- 使用内连接查询,新加的销售部没有
select * from dept d inner join emp e on e.dept_id = d.id;
-- 因为销售部不符合条件,如果要显示销售部怎么办
-- 使用左外连接查询
select * from dept d left join emp e on e.dept_id = d.id;
-- 因为销售部没有员工,所以员工使用空对应
-- 左连接好处:保证左表中的数据全部出现,在内连接的基础上让左表中的数据全部出现
右外连接
语法
select * from 左表 right join 右表 on 主表.主键=从表.外键
案例
-- 希望员工的信息全部显示出来
-- 使用内连接查询
select * from dept d inner join emp e on e.dept_id = d.id;
-- 使用右外连接查询
select * from dept d right join emp e on e.dept_id = d.id;
-- 右连接的好处:保证右表中的数据全部出现,左边没有对应的数据使用NULL
-- 要保证左右表中所有的数据都出现,使用全连接
-- select * from dept d full join emp e on e.dept_id = d.id;
-- 全连接在mysql中不支持,Oracle中支持
子查询的概念
- 一个查询的结果做为另一个查询的条件
- 子查询是存在查询的嵌套,内部的查询称为子查询,外部的查询就是父查询
- 子查询必须要使用括号
- 单行单列:父查询使用比较运算符
- 多行单列:父查询使用in, any, all
- 多行多列:父查询会做为一张虚拟表,再次进行查询
多表查询的规律
- 确定查询哪些表
- 确定表连接的条件
- 确定查询哪些列
- 如果还有条件,使用where进行过滤
索引查询数据方式
索引使用的是B树或B+树来存储,查询效率更高
- 索引表其实就是从原始表中抽取每个区域的一些数据组成一张表
- 查询的时候,先查询索引表,就可以确定记录在原始表中位置
- 再去查询原始表,通过2次查询可以找到记录
只要创建了索引表,在增删改原始表数据的时候,也可同时更新索引表中记录,如果一张表中索引表太多,维护成本也会更高,建议每表不超过5个索引
-- 对联系人的姓名列创建索引
create index ix_name on contact(name);
-- 显示指定表中所有的索引
show index from contact;
-- 删除上面创建的索引
drop index ix_name on contact; -- 本质上是删除了索引表
-- 显示指定表中所有的索引
show index from contact; -- 发现少了一个索引了
-- 创建名字和年龄的复合索引
create index ix_name_age on contact(name,age);
show index from contact;
-- 删除复合索引
drop index ix_name_age on contact;
-- 只要创建了索引,我们查询相应列的时候,就会自动使用索引去查询
索引的使用原则和不足
使用原则
- 要在数据量大的表上创建索引才有意义
- 在经常查询的字段(列)上使用索引,如:where子句后面的条件,或表连接on后面的条件上
- 如果一张表的修改频率非常高,有大量的增删改的操作,而很少有查询操作,不建议创建索引。
不足
- 表中不建议创建太多的索引,索引过多会导致增删改的效率降低,因为它需要同时更新所有的索引表。
- 因为索引也是一张表,每创建一个索引都会占用一定的硬盘空间。
索引失效的几种情况
失效的几种情况
- 如果查询条件中有or,即使其中有些条件创建了索引,索引也不起作用,除非你对or中每个查询列都创建了索引。(mysql中测试也是无效的)
- 模糊查询like,查询以’%xxx’开头的不使用索引,以’xxx%'结尾会使用索引
- 使用了比较运算符<>或!= 不等于的运算符,不使用索引
- mysql如果发现不使用索引更快,则不使用索引。
函数
目标
- 字符串函数
- 数学函数
- 日期函数
字符串函数
-- 字符串函数
-- 返回字符串的长度,所有的函数以玫瑰红显示
select char_length('Hello'); -- 5个
-- 用于拼接一个或多个字符串
select concat('I ', 'Love ', 'Java');
-- 将字符串转成小写
select lower('HELLO WORLD');
-- 将字符串转成大写
select upper('hello world');
-- 取子字符串:字符串, 开始位置(从1开始),长度
select substr('Hello World', 7, 3);
-- 去掉前后的空格
select trim(' Hello Java ');
数学函数
-- 返回[0,1) 的随机小数,包头不包尾
select rand();
-- 四舍五入保留几位小数
select round(1.2345,3);
-- 返回一个列表中最小值
select least(30,4,28,10,9);
-- 返回一个列表中最大值
select greatest(30,4,28,10,9);
日期函数
-- 日期函数
-- 计算某个时间加多少天以后得到一个新的日期
select adddate('2019-02-28', 7);
select adddate('2019-02-28', -7);
-- 返回当前日期
select curdate();
-- 两个日期之间相差多少天,用前面的减后面的
select datediff('2001-1-1', '2001-1-5');
-- 计算你到现在活了多少天
select datediff(curdate(), '1999-11-11');
-- 得到当前的日期和时间
select now();
事务的概述
什么是事务
如果一个业务操作需要执行多条SQL语句,必须使用事务。事务就是这多条SQL语句必须是一个整体,每条SQL语句都要执行成功,这个事务才提交。如果其中有任何一条SQL语句出现了异常执行失败,整个事务都要进行回滚。回到没有执行前的状态。
事务就是要么所有的SQL语句全部执行成功,要么全部执行失败。
事务的四大特性(ACID)
事务特性 | 含义 |
---|---|
原子性(Atomicity) | 一个事务所有的SQL语句是一个整体,不可分割。要么全部成功,要么全部失败。 |
一致性(Consistency) | 对数据库中数据的操作状态在事务执行前后必须是一致的, 如:转账前2个人的总金额与转账后2个人的总金额是一致的 |
隔离性(Isolation) | 每个表中同时有多个事务在执行,事务与事务之间是隔离的,不能相距影响 |
持久性(Durability) | 如果一个事务执行成功,对数据库的影响是持久的,服务器关闭也是存在的。 |
事务操作
事务的操作 | MySQL操作事务的语句 |
---|---|
开启事务 | start transaction |
提交事务 | commit |
回滚事务 | rollback |
设置回滚点 | savepoint 名字 |
回到回滚点 | rollback to 名字 |
查询事务的自动提交情况 | select @@autocommit |
设置事务的手动提交方式 | set @@autocommit |
并发访问的问题
目标
- 哪三种并发访问问题
- 哪四种隔离级别
并发访问的三个问题
什么是事务的并发访问:在同一个时间段,有多个用户开启了事务,并且同时访问同一个表中记录,称为事务的并发访问。指多个事务在并行的执行。
事务在操作时的理想状态是: 不存在并发访问的问题,在实际的应用过程中,因为事务隔离级别不同,会出现以下三种并发访问的问题。
并发访问的问题 | 含义 |
---|---|
脏读 | 一个事务读取到了另一个事务没有提交的数据 |
不可重复读 | 正常来说,一个事务多次读取同一条记录结果应该是一致的, 如果出现多次读取不一致的情况,称为不可重复读。通常是 因为一个事务在读取数据,另一个读取更新了这条记录导致。 |
幻读 | 一个事务在统计或查询的时候,2次出现查询的记录数不同。 通常是因为一个事务在统计,另一个事务插入或删除了记录导致。 |
因为会出现上面的三种并发访问的问题,所以我们需要避免这些问题的发生,避免的方式就是设置事务的隔离级别。
隔离级别越高,并发出现的问题就越少。在关系型数据库中一共有四种隔离级别。
隔离级别与安全性、性能的关系
隔离级别越高:安全性越高,性能就越低。
-
什么是不可重复读,如何解决?
一个事务多次读取的数据不同,解决方式就是提高隔离级别到repeatable read。
-
什么是幻读,如何解决幻读?
一个事务多次统计数据,得到数据条数不同。通常是由其它事务进行插入或删除操作导致。解决方式就是提高隔离级别为serializable。
-
什么是脏读?如何解决脏读的问题?
一个事务读取到另一个事务未提交的数据,解决方法就是提高事务的隔离级别为read committed。