Bootstrap

【MySQL】数据库的学习之路

在这里插入图片描述

文章目录

第0天 2023/12/06

数据库概述

数据库?

英文单词DataBase,简称DB按照一定格式存储数据的一些文件的组合
顾名思义:存储数据的仓库,实际上就是一堆文件这些文件中存储了具有特定格式的数据

数据库管理系统?

DataBaseManagement,简称DBMS
数据库管理系统是专门用来管理数据库中数据的,数据库管理系统可以对数据库当中的数据进行增删改查

常见的数据库管理系统:
MySQL、Oracle、MS SqlServer、DB2、sybase等…

SQL?

结构化查询语言

SQL是一套标准

关系是什么?

DBMS–执行–> SQL --操作–> DB

第1天 2023/12/07

SQL

表?

行(row):称为记录/字段

列(column):称为字段

每一个字段应该包括哪些属性?

​ 字段名、数据类型、相关的约束

SQL语句的分类

名称功能
DQL(数据查询语言)查询语句,凡是select语句都是DQL
DML(数据操作语言)insert delete update,对表当中的数据进行增删改
DDL(数据定义语言)create drop alter,对表结构的增删改
TCL(事务控制语言)commit提交事务,rollback回滚事务
DCL(数据控制语言)grant授权、revoke撤销权限等

DQL数据查询语句

简单查询

查询一个字段
select 字段名 from 表名;

select 和 from 都是关键字,字段名和表名都是标识符

不区分大小写,用分号结尾

查询两个字段
select 字段名,字段名 from 表名;
查询所有

方式一

select * from 表名;

缺点是效率比较低,在实际开发中不建议,自己快速看数据可以采用

方式二

select 每一个字段 from 表名;
给查询的列起别名

用as关键字,或者直接空格(如果表名中有空格用单引号‘隔开)

select 字段名 as 别名 from 表名;
select 字段名 '别名' from 表名;
使用数学表达式(计算年薪)

可以直接使用数学运算

select sal*12 as yearsql from emp;
select sal*12 as '年薪' from emp;

别名是中文用单引号

条件查询

查询出来符合条件的

语法格式

select
	字段
from
	表名
where
	条件;
条件语句

between … and… 等同于 >= and <=(左闭右闭)

between and除了可以使用在数字方面之外,还可以使用在字符串方面(左闭右开)

select ename from emp where ename between 'A’ and 'C';(首字母范围)

is nullis not null

andor

同时出现的优先级问题:and优先级比or高,先执行and,要加括号

and和or联合起来用:找出薪资大于1000的并且部门编号是20或30部门的员工
			select ename,sal,deptno from emp where sal > 1000 and deptno = 20 or deptno = 30; // 错误的
			select ename,sal,deptno from emp where sal > 1000 and (deptno = 20 or deptno = 30); // 正确的

in 包含 相当于多个or,也有not in

in等同于or:找出工作岗位是MANAGER和SALESMAN的员工?
			select ename,job from emp where job = 'SALESMAN' or job = 'MANAGER';
			select ename,job from emp where job in('SALESMAN', 'MANAGER');

like 模糊查询

%代表任意多个字符

_ 代表一个字符

找出名字中第二个字母是A的?
select ename from emp where ename like '_A%';

\转义

找出名字中带有下划线
select name from t_user where name like '%\_%';

排序

默认是升序的

select 
	ename,sal 
from 
	emp 
order by
	sal (asc);

需要指定降序

select 
	ename,sal 
from 
	emp 
order by
	sal desc;

ascend上升 descend下降

两个字段排序
按照工资的降序排列,当工资相同的时候再按照名字的升序排列
select ename,sal from emp order by sal desc;
select ename,sal from emp order by sal desc , ename asc;

注意:越靠前的字段越能起到主导作用只有当前面的字段无法完成排序的时候,才会启用后面的字段

语句执行顺序不能变

找出工作岗位是SALESMAN的员工,并且要求按照薪资的降序排列

select 
	ename,job,sal
from
     emp
where 
	job = 'SALESMAN'
order by
	sal desc;

单行处理函数

特点是一个输入对应一个输出

多行处理函数是多个输入对应一个输出

  • 转大小写 lower() upper()
员工名字改为小写
select lower(ename) as ename form emp;
  • 取子串 substr(被截取的字符串,起始下标,截取的长度)

起始下标从1开始,没有0

  • 拼接 concat( , )
  • 取长度 length()
取名字长度
select length(ename) as enamelength from emp;
  • 去除前后空白 trim()
  • 四舍五入 rand(变量, 保留到小数点几位)

比如对工资取整数就是rand(sal,0)

  • 生成随机数 rand()
  • 空处理函数 ifnull(数据,被当作哪个值)

可以将null转换成一个数值

ifnull(sal,0) 如果工资为null就为0

  • case…when…then…when…then…else…end
但员工的工作岗位是MANAGER的时候工资上调10%,当工作岗位是SALESMAN的时候,工资上调50%,其他正常
select
	ename,job,
	(case job when 'MANAGER' then sal*1.1 when 'SALESMAN' then sal*1.5 else sal end) as newsal
from
	emp;

分组函数(多行处理函数)

特点:输入多行,输出一行

分组函数在使用的时候必须先进行分组,然后才能用

如何没有对数据进行分组,默认整张表为一组

5个:

  • count 计数
  • sum 求和
  • avg 平均值
  • max 最大值
  • min 最小值
找出工资总和?
	select sum(sal) from emp;
找出最高工资?
	select max(sal) from emp;
找出最低工资?
	select min(sal) from emp;
找出平均工资?
	select avg(sal) from emp;
找出总人数?
	select count(*) from emp;
	select count(ename) from emp;

需要注意的

  1. 自动忽略null

    count(*)和count(具体字段)的区别

    count(具体字段):字段不为空的总数

    count(*)是总数

  2. 分组函数不能直接使用在where子句当中

    找出比最低工资高的员工信息
    select ename from emp where sal>min(sal);
    不能这样!!!
    
  3. 所有的分组函数都可以组合起来同时使用

分组查询 group by

select
	...
from
	...
where
	...
group by
	...
order by
	...

执行顺序:

  1. from
  2. where
  3. group by
  4. select
  5. order by
为什么分组函数不能直接用在where之后?
select ename from emp where sal>min(sal);

因为分组函数在使用的时候必须先分组之后才能使用 where执行的时候,还没有分组所以where后面不能出现分组函数

找出每个工作岗位的最高薪资
select max(sal),job from emp group by job;

select之后只可以添加group by的字段和分组函数

多个字段联合分组

找出每个部门不同工作岗位的最高薪资

select 
	deptno,job,max(sal)
from
	emp
group by
	deptno,job;

having

对分完组的数据进一步过滤,必须和group by联合使用

找出每个部门的最高薪资,要求显示薪资大于2900的数据
		第一步:找出每个部门的最高薪资
		select max(sal),deptno from emp group by deptno;
		+----------+--------+
		| max(sal) | deptno |
		+----------+--------+
		|  5000.00 |     10 |
		|  3000.00 |     20 |
		|  2850.00 |     30 |
		+----------+--------+

		第二步:找出薪资大于2900
		select max(sal),deptno from emp group by deptno having max(sal) > 2900; // 这种方式效率低
		+----------+--------+
		| max(sal) | deptno |
		+----------+--------+
		|  5000.00 |     10 |
		|  3000.00 |     20 |
		+----------+--------+

		select max(sal),deptno from emp where sal > 2900 group by deptno;  // 效率较高,建议能够使用where过滤的尽量使用where
		+----------+--------+
		| max(sal) | deptno |
		+----------+--------+
		|  5000.00 |     10 |
		|  3000.00 |     20 |
		+----------+--------+

优化策略:where和having,优先选择w’he

第2天 2023/12/08

去重 distinct关键字

select distinct job from emp; // distinct关键字去除重复记录
select ename,distinct job from emp;

以上的sql语句是错误的
记住:distinct只能出现在所有字段的最前面,进行联合去重

案例:统计岗位的数量?
select count(distinct job) from emp;

连接查询

多表查询

from

​ …

join

​ …
on

​ …

连接查询的分类
  1. 内连接
    1. 等值连接
    2. 非等值连接
    3. 自连接
  2. 外连接
    1. 左外连接(左连接)
    2. 右外连接(右连接)
  3. 全连接
笛卡尔积现象

不加条件进行连接,会出现笛卡尔积现象,需要加条件进行避免

表的连接次数越多,效率越低

内连接之等值连接
	select 
		e.ename,d.dname
	from
		emp e
	inner join //yinner可以省略,但是带着可读性更好
		dept d
	on
		e.deptno = d.deptno;
内连接之非等值连接

连接条件中的关系是非等量关系

找出每个员工的工资等级,要求显示员工名、工资、工资等级

select 
	e.ename,e.sal,s.grade
from
	emp e
inner join
	salgrade s
on
	e.sal between s.losal and s.hisal;
内连接之自连接

一张表看作两张表

找出每个员工的上级领导,要求显示员工名和对应的领导名
select 
	a.ename as '员工名',b.ename as '领导名'
from
	emp a
inner join
	emp b
on
	a.mgr = b.empno;
外连接
什么是外连接,和内连接有什么区别?

内连接:
假设A和B表进行连接,使用内连接的话,凡是A表和B表能够匹配上的记录查询出来,这就是内连接AB两张表没有主次之分,两张表是平等的

外连接:
假设A和B表进行连接,使用外连接的话,AB两张表中有一张表是主表,一张表是副表,主要查询主表中的数据,捎带着查询副表,当副表中的数据没有和主表中的数据匹配上,副表自动模拟出NULL与之匹配

外连接的分类?
左外连接(左连接):表示左边的这张表是主表
右外连接(右连接):表示右边的这张表是主表

找出每个员工的上级领导?(所有员工必须全部查询出来)
select 
	a.ename '员工', b.ename '领导'
from
	emp a
left join //left outer join,outer可以省略
	emp b
on
	a.mgr = b.empno;
三张以上表连接
找出每一个员工的部门名称以及工资等级
select 
		e.ename,d.dname,s.grade
	from
		emp e
	join
		dept d
	on
		e.deptno = d.deptno
	join
		salgrade s
	on
		e.sal between s.losal and s.hisal;

子查询

什么是子查询?

​ select语句当中嵌套select语句,被嵌套的select语句是子查询

子查询可以出现在哪里?

有三种

​ select//了解即可
​ …(select).
​ from
​ …(select).
​ where
​ …(select).

where子句中的子查询

可以把查询结果用作条件

找出高于平均薪资的员工信息
select * from emp where sal > avg(sal); //错误的写法,where后面不能直接使用分组函数

第一步:找出平均薪资
	select avg(sal) from emp;

第二步:where过滤
	select * from emp where sal > 2073.214286;

第一步和第二步合并:
	select * from emp where sal > (select avg(sal) from emp);

from子句的子查询

可以将子查询的查询结果当作一张临时表

找出每个部门平均薪水的等级
	t.*,s.grade
from
	(select deptno,avg(sal) as avgsal from emp group by deptno) t
join
	salgrade s
on
	t.avgsal between s.losal and s.hisal;

avg(sal)要取别名,不然当作函数,无法执行

Union

合并查询结果集

找出工作岗位是SALESMAN和MANAGER的员工?

第一种:select ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
第二种:select ename,job from emp where job in('MANAGER','SALESMAN');
Union:
select ename,job from emp where job = 'MANAGER'
union
select ename,job from emp where job = 'SALESMAN';

union可以减少匹配的次数,union的效率更高

limit

limit是将查询结果集的一部分取出来,通常使用在分页查询中(重点!中重中之重!!)

limit是sql语句最后执行的一个环节:

	select		5
		...
	from		1
		...		
	where		2
		...
	group by	3
		...
	having		4
		...
	order by	6
		...
	limit		7
语法机制
	limit 起始下标, 长度

​ startIndex表示起始位置,从0开始,0表示第一条数据
​ length表示取几个

找出工资排名在第4到第9名的员工?
	select ename,sal from emp order by sal desc limit 3,6;
分页操作
第pageNo页:(pageNo - 1) * pageSize, pageSize
limit (pageNo - 1) * pageSize, pageSize

DQL总结

	select		5
		...
	from		1
		...		
	where		2
		...
	group by	3
		...
	having		4
		...
	order by	6
		...
	limit		7
		...

表的创建和数据类型

DDL数据定义语言包括:

create、drop、alter

建表语句的语法格式:
		create table 表名(
			字段名1 数据类型,
			字段名2 数据类型 default 默认值,
			字段名3 数据类型,
			....
		);

所有标识符要是小写,单词之间要用下划线

MySQL常见数据类型
数据类型功能
int整数型(java中的int)
bigint长整型(java中的long)
float浮点型(java中的float double)
char定长字符串(String)
varchar可变长字符串(StringBuffer/StringBuilder)
date日期类型 (对应Java中的java.sql.Date类型)
BLOB二进制大对象(存储图片、视频等流媒体信息) Binary Large OBject (对应java中的Object)
CLOB字符大对象(存储较大文本,比如,可以存储4G的字符串) Character Large OBject(对应java中的Object)
char和varchar怎么选择?

​ 在实际的开发中,当某个字段中的数据长度不发生改变的时候,是定长的,例如:性别、生日等都是采用char
​ 当一个字段的数据长度不确定,例如:简介、姓名等都是采用varchar

创建学生表:
		学生信息包括:
			学号、姓名、性别、班级编号、生日
			学号:bigint
			姓名:varchar
			性别:char
			班级编号:int
			生日:char
		
		create table t_student(
			no bigint,
			name varchar(255),
			sex char(1),
			classno varchar(255),
			birth char(10)
		);

表名在数据库当中一般建议以:t_或者tbl_开始

删除表 drop

drop table t_student;//这个不存在的话会报错
drop table if exists t_student;//这个如果不存在不会报错

插入insert

语法格式
insert into 表名(字段名1,字段名2,字段名3,....) values(值1,值2,值3,....);

要求:字段的数量和值的数量相同,并且数据类型要对应相同

字段名省略等于都写上

可以一次插入多条记录

insert into 表名(字段名1,字段名2,字段名3,....) values
(值1,值2,值3,....),
(值1,值2,值3,....),
(值1,值2,值3,....);
插入日期
str_to_date('01-10-1990','%d-%m-%Y');

如果是%Y-%m-%d可以不用str_to_date

date_format(数据,'日期格式');

默认格式%Y-%m-%d

date和datetime的区别

date:短日期,只包括年月日信息

默认:%Y-%m-%d

datetime:长日期,包括年月日时分秒信息

默认:%Y-%m-%d %h:%i:%s

now()函数会返回包括年月日时分秒信息的日期信息

修改update (DML)

语法格式
update 表名 set 字段名1=值1,字段名2=值2... where 条件;

将部门10的LOC修改为SHANGHAI,将部门名称修改为RENSHIBU
update dept1 set loc = 'SHANGHAI', dname = 'RENSHIBU' where deptno = 10;

​ 注意:没有条件整张表数据全部更新

删除delete & truncate (DML)

语法格式
delete from 表名 where 条件;

注意:没有条件全部删除

支持回滚,delete原理是数据被删除但是数据在硬盘上的真是存储空间不会被释放,但是缺点删除效率比较低

怎么删除大表中的数据?(重点)
truncate table 表名; // 表被截断,不可回滚永久丢失

复制表

create table emp3 as select * from emp;

包含表内的信息全部复制

create table emp3 as select * from emp where job = 'MANAGER';

复制部分信息

insert into dept_bak select * from dept;

在原有表的基础上,增加数据条数,要求格式要一致

约束

constraint

创建表的时候,可以给表的字段添加相应的约束,添加约束的目的是为了保证表中数据的合法性、有效性、完整性

第4天 2023/12/10

常见的约束
  • 非空约束

    not null,不能为空

  • 唯一性约束

    unique,但可以为NULL

    联合起来唯一

    drop table if exists t_user;
    	create table t_user(
    		id int, 
    		usercode varchar(255),
    		username varchar(255),
    		unique(usercode,username) // 多个字段联合起来添加1个约束unique 【表级约束】
    	);
    

    列级约束 只在列后面添加

    表级约束 需要给多个字段联合起来添加一个约束

  • 主键约束 primary key

    主键的特征:not null + unique

    主键不能是NULL,也不能重复

    id int primary key auto_increment, // id字段自动维护一个自增的数字,从1开始,以1递增
    

    也可以添加表级约束,复合主键

    主键类型不建议varchar,一般为数字

    自然主键:主键值最好就是一个和业务没有任何关系的自然数(这种方式是推荐的)
    业务主键:主键值和系统的业务挂钩,例如:拿着银行卡的卡号做主键,拿着身份证号码作为主键(不推荐用)

    最好不要拿着和业务挂钩的字段作为主键因为以后的业务一旦发生改变的时候,主键值可能也需要随着发生变化,但有的时候没有办法变化,因为变化可能会导致主键值重复

  • 外键约束 foregin key

    外键可以为null,被引用的不一定是主键,但是要有唯一性

    为了保证保证classno是引用cno

    create table t_student(
    			sno int,
    			sname varchar(255),
    			classno int,
    			primary key(sno),
    			foreign key(classno) references t_class(cno)
    		);
    

    顺序要求
    删除数据的时候,先删除子表,再删除父表
    添加数据的时候,先添加父表,在添加子表
    创建表的时候,先创建父表,再创建子表
    删除表的时候,先删除子表,在删除父表

  • 检查约束 check(mysql不支持,oracle支持)

存储引擎(了解)

是MySQL中特有的一个术语,其他的数据库中没有

Oracle中有,但是不叫这个名字

存储引擎是一个表存储/组织数据的方式

不同的存储引擎,表存储数据的方式不同

如何给表添加/指定存储引擎

在建表的时候给表指定存储引擎,在小括号的外面

ENGINE来指定存储引擎

CHARSET指定编码方式

CREATE TABLE `t_x` (
	  `id` int(11) DEFAULT NULL
	) ENGINE=InnoDB DEFAULT CHARSET=utf8;

MySQL常用的存储引擎
MyISAM

MyISAM是mysql最常用的存储引擎,但是这种引擎不是默认的
MyISAM采用三个文件组织一张表:
xxx.frm(存储格式的文件)
xxx.MYD(存储表中数据的文件)
xxx.MYI(存储表中索引的文件)

优点:可被压缩,节省存储空间并且可以转换为只读表,提高检索效率
缺点不支持事务

InnoDB

这是MySQL默认的存储引擎,是一个重量级引擎

  • 支持事务、行级锁、外键等,这种存储引擎数据的安全得到保障
  • 表的结构存储在xxx.frm文件中
  • 数据存储在tablespace这样的表空间中(逻辑概念),无法被压缩,无法转换成只读
  • 这种InnoDB存储引擎在MySQL数据库崩溃之后提供自动恢复机制
  • InnoDB支持级联删除级联更新
MEMORY

缺点:不支持事务;因为所有数据和索引都是存储在内存当中的,数据容易丢失
优点:查询速度最快

事务

transaction

一个事务其实就是一个完整的业务逻辑,保证批量的DML语句同时成功,或者同时失败

什么是一个完整的业务逻辑?

​ 假设转账

​ A账户减去10000;

​ B账户加上10000;

以上就是一个完整的业务逻辑,是一个最小的工作单元,不可再分

只有DML语句才会有事务这一说

insert / delete / update

实现原理

事务如何做到多条DML的同时成功和失败?
InnoDB存储引擎:提供了一组用来记录事务性活动的日志文件

事务开启了:

​ insert

​ insert

​ …

事务结束

在十五执行过程当中,每一条DML都会记录到事务性活动的日志文件当中,在事务的执行过程当中,我们可以提交事务回滚事务

 start transaction;
提交事务

清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中

提交事务标志着事物的结束,并且是一种全部成功的结束

commit;
回滚事务

将之前的所有的DML操作全部撤销,并且清空事务性活动的日志文件

回滚事务标志着事物的结束,是一种全部失败的结束

rollback;
事务特性

事务包括四大特性:ACID
A: 原子性:事务是最小的工作单元,不可再分
C: 一致性:事务必须保证多条DML语句同时成功或者同时失败
I:隔离性:事务A与事务B之间具有隔离
D:持久性:持久性说的是最终数据必须持久化到硬盘文件中,事务才算成功的结束

事务的隔离性
事务隔离的四个级别
  1. 第一级别:读未提交(read uncommitted)
    对方事务还没有提交,我们当前事务可以读取到对方未提交的数据。读未提交存在脏读(Dirty Read)现象:表示读到了脏的数据。

  2. 第二级别:读已提交(read committed)
    对方事务提交之后的数据我方可以读取到,这种隔离级别解决了: 脏读现象没有了。读已提交存在的问题是:不可重复读。

  3. 第三级别:可重复读(repeatable read)
    事务开启后,不管是多久,每一次在事务中读取的数据都是一致的。这种隔离级别解决了:不可重复读问题。这种隔离级别存在的问题是:幻影读,读取到的数据是幻象
    提交数据之后也读不到,永远是刚开启事务时的数据

  4. 第四级别:序列化读/串行化读(serializable)
    解决了所有问题,效率低。需要事务排队,不能并发,每一次读到的数据都是真实的

演示第1级别:读未提交
	set global transaction isolation level read uncommitted;
演示第2级别:读已提交
	set global transaction isolation level read committed;
演示第3级别:可重复读
	set global transaction isolation level repeatable read;

索引

索引时在数据库的字段上添加的,是为了提高查询效率存在的一种机制

一张表的一个字段可以添加一个索引,多个字段联合起来也可以添加索引,索引相当于新华字典的目录,是为了缩小范围而存在的一种机制

在数据库方面,查询一张表的时候有两种检索方式:
第一种方式:全表扫描
第二种方式:根据索引检索(效率很高)

索引是各种数据库进行优化的重要手段,优化的时候优先考虑索引

select ename,sal from emp where ename = 'SMITH';
		当ename字段上没有添加索引的时候,以上sql语句会进行全表扫描,扫描ename字段中所有的值。
		当ename字段上添加索引的时候,以上sql语句会根据索引扫描,快速定位。
索引实现原理

平衡二叉树

提示1:数据库的主键会自动添加索引,MySQL如果有unique约束,也会有创建索引对象

提示2:在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号

提示3:在MySQL中,索引是一个单独的对象,不同的存储引擎以不同的形式存在,

​ MyISAM中:存储在.MYI文件,

​ InnoDB中:索引存储在一个逻辑名称叫做tablespace的当中

​ MEMORY中:存储在内存当中

不管索引存储在哪里,索引在MySQL当中都是以一个树的形式存在(平衡二叉树:B-Tree)

缩小扫描范围,快速定位平衡二叉树的位置,平衡二叉树记录物理编号

什么时候考虑添加索引?
  1. 条件一:数据量庞大(这个需要实际测试)
  2. 条件二:该字段经常出现在where后面
  3. 条件三:该字段很少DML操作,因为DML之后,索引需要重新排序

不要随意提添加索引,因为索引需要维护,太多反而降低系统性能。

创建索引对象
create index 索引名称 on 表名(字段名);
create indiex emp_ename_index on emp(ename);爱张紫仪三千年
删除索引对象
drop index 索引名称 on 表名;
查看sql语句是否使用索引
 explain select ename,sal from emp where sal = 5000;
索引失效
select ename from emp where ename like '%A%';

​ 模糊查询的时候,第一个通配符使用的是%,这个时候索引是失效的。

视图

view:站在不同角度去看待同一份数据

创建视图

只有DQL语句才能创建

create view myview as select empno,ename from emp;

删除视图

drop view myview;

视图的作用

可以对视图进行增删改查,对视图对象的增删改查会导致原表被操作

可以简化开发,不用反复编写需要复用的SQL语句

数据库设计范式

避免数据冗余、空间浪费

第一范式

任何一张表都应该有主键,并且每一个字段原子性不可再分

第二范式

建立在第一范式的基础之上,所有非主键字段完全依赖主键,不能产生部分依赖,主键不是

多对多,三张表,关系表两个外键

第三范式

建立在第二范式的基础之上,所有非主键字段直接依赖主键不能产生传递依赖

一对多,两张表,多的表上加外键

实际开发会拿冗余换执行速度,因为在SQL中,表和表连接次数越多,效率越低,存在冗余可以减少表的连接次数

;