本文是以下课程的课程笔记:MySQL入门基础,mysql基础视频+数据库实战,老杜带你学
重点在于SQL查询语言,还需理解存储引擎、事务、索引、视图、数据库设计三范式等内容,面试可能会问到。
可略过
基本用法介绍
https://zhuanlan.zhihu.com/p/188416607
https://blog.csdn.net/liuzuoping/article/details/101931559 创建了配置文件
# 启动mysql服务,如果已经启动,则这步会报错,通过services.msc查看mysql是否启动,如果启动,则不需要执行这步;或者尝试关闭 net stop mysql ,之后再启动
net start mysql
# 进入数据库
mysql -u 用户名 -p 密码
# 我的windows上的mysql这样启动
mysql -uroot -p123456
# 看看数据库
show databases;
# 使用数据库
use [databasename];
# 查看某个数据库下有哪些表
show tables
# ......
# 操作数据库(具体命令见下面的“sql语言”)
# ......
# 批量地执行sql语句
# 将语句保存在 .sql文件中
source filename.sql
# 退出数据库
exit
# 关闭mysql服务
net stop mysql
格式:select 字段 from 表 where 条件语句;
例1:查找工资等于800的员工编号和姓名 :
select empno, ename from emp where sal = 800;
例2:查找工资不等于800的员工编号和姓名 :
select empno, ename from emp where sal <> 800;
例3:查找工资在2450到3000之间的员工编号和姓名 :
select empno, ename from emp where sal between 2450 and 3000; 或者 select empno, ename from emp where sal >= 2450 and sal <= 3000;
例4:查找补助为空的员工编号和姓名 :
select empno, ename from emp where comm is null;
例4:查找补助不为空的员工编号和姓名 :select empno, ename from emp where comm is not null;
例5:两个条件,是并且的关系用and,是或者的关系用or
查找工作岗位是manager和salesman的员工
select empno, ename,job from emp where job = ‘MANAGER’ or job = ‘SALESMAN’;
例6:and和or的优先级问题
查询工资大于2500,并且部门编号为10或20的员工
select empno,ename from emp where sal > 2500 and (depno = 10 or depno = 20);
in 相当于多个or
查询工作岗位是manager 和 salesman的员工
select empno,ename from emp where job = ‘MANAGER’ or job = ‘SALESMAN’
或者
select empno, ename, job from emp where job in (‘MANAGER’,‘SALESMAN’);
not in 相当于 in 取反
模糊查询:%表示任意多个字符,_表示任意一个字符
例子:找出名字以T结尾的
select ename from emp where ename like ‘%T’;
例子:找出第二个字母是A的
select ename from emp where ename like ‘_A%’;
例子:找出名字中有_的(用转义字符\来表示出)
select ename from emp where ename like ‘%\_%’;
查询所有员工薪资、排序
select ename,sal from emp order by sal
降序
select ename,sal from emp order by sal desc
按照多个字段排序
例子:查询员工名字和薪资,按照薪资升序排序,如果一样就按照名字的升序排序
select ename,sal from emp order by sal,ename
综合案例:找出工资在1250和3000之间的员工信息,要求按照薪资降序排列
select ename, sal from emp where sal between 1250 and 3000 order by sal desc;
说明:单行处理函数是“一个数据对应一个输出”,多行处理函数是“多个输入对应一个输出“
转换小写lower
ex:select lower(ename) as ename from emp;
转换大写upper
字符串取长度 length
取子串 substr,开始下标为1
ex: 找出员工名字第一个字母是A的员工的信息
select ename from emp where substr(ename,1,1) = ‘A’;
字符串拼接concat
ex: 首字母大写
select concat(upper(substr(ename,1,1)) ,lower(substr(ename,2,length(ename)-1))) as ename from emp ;
去空格 trim
ex:select * from emp where ename = trim(’ ING ');
四舍五入 round(字面值,保留小数个数)
select后面可以加表的字段名或者字面值(也就是数据),当加的是字面值时会查询出一列,其中值都是这个字面值
ex: select round(1236.567,0) as result from emp;
生成随机数 rand()
ex: 生成100以内随机整数
select round(rand()*100,0) from emp;
将null转化为一个具体值 ifnull
在所有数据中只要有null参与的数学运算,最终结果都是null
ex: 计算每个员工的年薪(因为补助有可能为null,所以要将null变成0)
select ename, sal + ifnull(comm,0) as yearsal from emp;
case…when…then…when… then…else…end
ex:当员工的工作岗位是manager时工资上调10%,当工作岗位是salesman时工资上调50%,其他正常
select ename,job,sal as oldsal, (case job when 'MANAGER' then sal*1.1 when 'SALESMAN' then sal*1.5 else sal end) as newsal from emp;
如果没有对数据分组,整张表默认为一组
有5个:count,min,max, sum, avg
自动忽略null,不需要对null处理,这里与数据处理函数不同
分组函数中count(*)和count(具体字段) 的区别
count(*):统计表中总行数(因为不可能存在全部是null的记录,所以这里不讨论是否统计null行)
count(具体字段) :统计该字段下所有不为null的元素的总数
分组函数不能直接使用在where子句中
例如下面的例子是无法运行的:select ename,sal from emp where sal > min(sal)
原因如下:因为分组函数在使用前必须先进行分组(即group by的操作),而sql的执行顺序是from→where→group by → select → order by,其中where在group by之前执行
写sql语句的顺序:
select
...
from
a
join // 默认是inner join,还有右外连接right join,左外连接left join
b
on
a和b的连接条件
join
c
on
a和c的连接条件
where
...
group by
...
having
...
order by
...
执行sql语句的顺序:
ex:计算每个工作岗位的工资和
select job,sum(sal) from emp group by job
执行过程:
select只能跟“参加分组的字段”和“分组函数”,不可以这样写:select ename,job,sum(sal) from emp group by job,因为是按照job来分组的,ename在分组过程中已经被消除了。
ex:找出每个部门、不同工作岗位的最高薪资
技巧:两个字段联合成一个字段看(两个字段联合分组)
select deptno,job,max(sal) from emp group by deptno,job;
ex:找出每个部门最高薪资,要求显示最高薪资大于3000的
使用having对group by后的数据过滤(不可以用where,之前已经说过了)
select deptno, max(sal) as maxsal from emp group by deptno having maxsal >= 3000;
以上sql语句效率低,可以先找出工资大于3000的,然后再分组
select deptno, max(sal) as maxsal from emp where sal >= 3000 group by deptno;
所以能用where的尽量用,不得已再用having(尽可能在group by 分组前过滤)
ex:找出每个部门平均薪资,要求显示平均薪资高于2500的
select deptno, avg(sal) as avgsal from emp group by deptno having avgsal > 2500;
ex:找出每个工作岗位的平均薪资,要求显示平均薪资大于1500的,除manager岗位之外,要求按照平均薪资降序排序
select job, avg(sal) as avgsal from emp where job not in (‘MANAGER’) group by job having avgsal > 1500 order by avgsal desc;
distinct只能出现在所有字段的最前面
select distinct job,deptno from emp;
统计工作岗位的数量
select count(distinct job) from emp;
定义:从一张表中单独查询,称为单表查询,emp表和dept表联合起来查询数据,从emp表中取员工名字,从dept表中取出部门名字,这种跨表查询、多张表联合起来查询数据,被称为 连接查询
分类:
根据语法的年代分类:
根据表连接的方式分类:
内连接
外连接
左外连接(左连接)
右外连接(右连接)
全连接(不讲)
笛卡尔积:两张表没有任何限制地连接时,表一的每一个字段和表二的所有字段都产生连接,总共产生的条目数是表一条目数 * 表二条目数
怎么避免笛卡尔积:表连接时加条件
select ename, dname from emp, dept where emp.deptno = dept.deptno
但连接时匹配的次数没有减少,只是最后显示的字段变少了
为了提高效率,要为表起别名,并在select中用上,上述查询语句可以改进为:
select e.ename, d.dname from emp as e, dept as d where e.deptno = d.deptno
通过笛卡尔积现象得出,表的连接次数越多,效率越低,所以要尽量减少连接
内连接——等值连接(on后的条件是等量条件)
ex:查询每个员工所在部门名称,显示员工名和部门名
SQL92:select e.ename, d.dname from emp as e, dept as d where e.deptno = d.deptno;
SQL99:select e.ename, d.dname from emp as e join dept as d on e.deptno = d.deptno;
SQL99将表连接和后续的where 过滤分离,我们用这种
内连接——非等值连接(on后的条件是非等量条件)
ex:找出每个员工的薪资等级,要求显示员工名、薪资、薪资等级
select e.ename,e.sal,s.grade from emp as e join salgrade as s on e.sal between s.losal and s.hisal;
内连接——自连接
ex:查询员工的上级领导,要求显示员工名和对应的领导名称
select e1.ename,e2.ename from emp as e1 join emp as e2 on e1.mgr = e2.empno order by e2.ename;
外连接
内连接是完全能够匹配上on后条件的数据被查询出来。
右外连接是将join右边的表作为主要的表,不管右边的表是否满足on后的条件,都要查询出来;左边的表此时为次要的表,只有满足on后的条件才能被查出来。
左外连接和右外连接相反。
外连接的查询结果的条数一定是大于等于内连接的
ex:查询每个员工的上级领导,要求显示所有员工的名字和领导名
select e1.ename, e2.ename
from emp as e1 left
join emp as e2
on e1.mgr = e2.empno ;
多表连接
ex:找出每个员工的部门名称、工资等级、上级领导,要求显示员工名、领导名、部门名、薪资和薪资等级
select e1.ename, e2.ename, d.dname, e1.sal, s.grade
from emp as e1
left join emp as e2
on e1.mgr = e2.empno
join dept as d
on e1.deptno = d.deptno
join salgrade as s
on e1.sal between s.losal and s.hisal;
定义:select中嵌套了select语句
where子句中出现子查询
ex:找出比最低工资高的员工姓名和工资
select ename,sal from emp where sal > (select min(sal) from emp)
☆from子句中的子查询
技巧:from后面的子查询可以将子查询的结果当做一张临时表
ex:找出每个岗位的平均工资的薪资等级
思路:先找出每个岗位的平均工资(按照岗位分组求平均值),可以将上面的查询结构看成一个真实存在的表,记为表t,将t和salgrade表连接,找到薪资等级
第一步先写出sql的基本结构,用t代表子查询后形成的表 :select t.*, s.grade from t join salgrade s on t.avg(sal) between s.losal and s.hisal;
第二步完善t:select t.job, t.avgsal, s.grade from (select job, avg(sal) as avgsal from emp group by job) as t join salgrade s on t.avgsal between s.losal and s.hisal;
union比join效率高,将乘法变成加法
ex:查询工作岗位是manager和salsman的员工
select ename from emp where job in (‘MANAGER’,‘SALESMAN’);
或者
select ename from emp where job = ‘MANAGER’
union
select ename from emp where job = ‘SALESMAN’
union要求两个结果集列数相同、数据类型相同
通常使用在分页查询中显示,为了提高用户体验
ex:查找工资最高的5名员工
select ename, sal from emp order by sal desc limit 0,5;
ex:取出工资排名在5-9名的员工
select ename, sal from emp order by sal desc limit 4,5;
加⭐️是我不会做的
⭐️取得每个部门最高薪水的人员名称
【分析】1. 取得每个部门最高薪水,没有想到用group by
2. 取得人员名称:将第一步查询的结果和emp表做连接查询
【解】
select t.deptno, t.maxsal, e.ename
from emp e
join (select deptno,max(sal) as maxsal from emp group by deptno) t
on t.deptno = e.deptno and t.maxsal = e.sal;
哪些人的薪水在部门的平均薪水之上
【分析】1. 取得平均薪水
2. 找到在平均薪水之上的人
【解】
select e1.ename, e1.deptno, e1.sal
from emp e1
join (select deptno, avg(sal) as avgsal from emp group by deptno) e2
on e1.deptno = e2.deptno and e1.sal > e2.avgsal;
⭐️取得部门中(所有人的)平均的薪水等级
【分析】
【解】
select e.deptno, avg(s.grade)
from emp e
join salgrade s
on e.sal between s.losal and s.hisal
group by e.deptno;
⭐️不准用组函数(Max ),取得最高薪水
【分析】
【解】
select ename, sal
from emp
order by sal desc
limit 1;
select sal
from emp
where sal not in
(select a.sal from emp a join emp b on a.sal < b.sal);
取得平均薪水最高的部门的部门编号
【分析】
【解】
select deptno, avg(sal) as avgsal
from emp
group by deptno
order by avgsal desc
limit 1;
取得平均薪水最高的部门的部门名称
【分析】
【解】
select d.dname, e.avgsal
from dept as d
join
(select deptno, avg(sal) as avgsal
from emp
group by deptno
order by avgsal desc
limit 1) as e
on e.deptno = d.deptno;
求平均薪水的等级最低的部门的部门名称
【分析】
【解】
select d.dname, s.grade
from salgrade as s
join
(select deptno, avg(sal) as avgsal
from emp
group by deptno) as e
on e.avgsal between s.losal and s.hisal
join dept as d
on d.deptno = e.deptno
order by s.grade
limit 1;
⭐️取得比普通员工(员工代码没有在 mgr 字段上出现的) 的最高薪水还要高的领导人姓名
【分析】
【解】
select e1.ename
from emp as e1
join
(select sal as maxsal
from emp
where empno not in (select distinct mgr from emp ==where mgr is not null==)
order by sal desc
limit 1) as e2
on e1.sal > e2.maxsal;
取得薪水最高的前五名员工
select ename, sal
from emp
order by sal desc
limit 5;
取得薪水最高的第六到第十名员工
select ename, sal
from emp
order by sal desc
limit 5,5;
取得最后入职的 5 名员工
select ename,hiredate
from emp
order by hiredate desc
limit 5;
取得每个薪水等级有多少员工
【分析】
【解】
select s.grade,count(e.empno)
from emp as e
join salgrade as s
on e.sal between s.losal and s.hisal
group by s.grade;
有 3 个表 S(学生表),C(课程表),SC(学生选课表)
S(SNO,SNAME)代表(学号,姓名)
C(CNO,CNAME,CTEACHER)代表(课号,课名,教师)
SC(SNO,CNO,SCGRADE)代表(学号,课号,成绩)
问题:
1,找出没选过“黎明”老师的所有学生姓名。
select s.sname
from s
join sc
on s.sno = sc.sno
where sc.cno not in (select cno from c where cteacher = '黎明');
2,列出 2 门以上(含2 门)不及格学生姓名及平均成绩。
3,即学过 1 号课程又学过 2 号课所有学生的姓名。
列出所有员工及领导的姓名
select e1.ename, e2.ename
from emp as e1
left join emp as e2
on e1.mgr = e2.empno;
列出受雇日期早于其直接上级的所有员工的编号,姓名,部门名称
select e1.empno, e1.ename, d.dname
from emp e1
join emp e2
on e1.mgr = e2.empno and e1.hiredate < e2.hiredate
join dept d
on d.deptno = e1.deptno;
列出部门名称和这些部门的员工信息, 同时列出那些没有员工的部门
```sql
select d.dname, e.empno, e.ename
from dept d
left join emp e
on e.deptno = d.deptno;
```
⭐️列出至少有 5 个员工的所有部门
【分析】
【解】
方法一:
select deptno
from emp
group by deptno
having count(*) >= 5;
方法二:
select e.deptno, e.num
from
(select deptno,count(empno) as num
from emp
group by deptno) as e
where e.num >= 5;
列出薪金比"SMITH" 多的所有员工信息
方法一:用子查询(更好)
select ename,sal from emp where sal > (select sal from emp where ename = 'SMITH');
方法二:用表连接
select e1.ename,e1.sal
from emp as e1
join emp as e2
on e2.ename = 'SMITH' and e2.sal < e1.sal;
列出所有"CLERK"( 办事员) 的姓名及其部门名称, 部门的人数
```sql
select e.ename, d2.dname,d1.num
from emp as e
join
(select deptno,count(empno) as num from emp group by deptno) as d1
on e.deptno = d1.deptno
join dept as d2
on d2.deptno = e.deptno
where e.job = 'CLERK';
```
列出最低薪金大于 1500 的各种工作 及 从事此工作的全部雇员人数按照工作岗位分组求最小值。
【解】
select job,count(*) as num
from emp
group by job
having min(sal) > 1500
order by num
limit 1;
⭐️列出在部门"SALES"< 销售部> 工作的员工的姓名, 假定不知道销售部的部门编号.
【分析】用在where中的子查询很常见
select ename
from emp
where deptno = (select deptno from dept where dname = 'SALES');
列出薪金高于公司平均薪金的所有员工, 所在部门, 上级领导, 雇员的工资等级.
select e1.ename,d.dname,e2.ename,s.grade
from emp as e1
join emp as e2
on e1.mgr = e2.empno
join salgrade as s
on e1.sal between s.losal and s.hisal
join dept as d
on d.deptno = e1.deptno
where e1.sal > (select avg(sal) from emp)
⭐️列出与"SCOTT" 从事相同工作的所有员工及部门名称
【分析】一个细节上的错误,在where后要加e.ename <> ‘SCOTT’;
select e.ename, d.dname
from emp as e
join dept as d
on e.deptno = d.deptno
where e.job = (select job from emp where ename = 'SCOTT') and e.ename <> 'SCOTT';
⭐️列出薪金等于部门 30 中员工的薪金的其他员工的姓名和薪金.
【分析】忘记了可以用in
方法一:
select ename, sal
from emp
where sal in (select sal from emp where deptno = 30) and deptno <>30;
方法二:
select e1.ename, e1.sal
from emp e1
join
(select sal from emp where deptno = 30) as e2
on e1.sal = e2.sal and e1.deptno<>30;
列出薪金高于在部门 30 工作的所有员工的薪金的员工姓名和薪金. 部门名称
select e.ename, e.sal, d.dname
from emp as e
join dept as d
on e.deptno = d.deptno
where e.sal > (select max(sal) from emp where deptno = 30);
⭐️列出在每个部门工作的员工数量, 平均工资和平均服务期限
【分析】
服务期限:在mysql当中怎么计算两个日期的“年差”:TimeStampDiff(间隔类型, 前一个日期, 后一个日期
间隔类型:
SECOND 秒,
MINUTE 分钟,
HOUR 小时,
DAY 天,
WEEK 星期
MONTH 月,
QUARTER 季度,
YEAR 年
⭐️必须要right join dept,因为有人数为0的部门
错误的写法:
select
count(ename),
ifnull(avg(sal),0) as avgsal,
ifnull(avg(timestampdiff(YEAR, hiredate, now())), 0) as avgservicetime
from emp
group by deptno;
正确的写法:
select
d.deptno,
count(e.ename) ecount,
ifnull(avg(e.sal),0) as avgsal,
ifnull(avg(timestampdiff(YEAR, hiredate, now())), 0) as avgservicetime
from emp e
right join dept d
on e.deptno = d.deptno
group by d.deptno;
列出所有员工的姓名、部门名称和工资。
select e.ename, d.dname, e.sal
from emp as e
left join dept as d
on e.deptno = d.deptno;
⭐️列出所有部门的详细信息和人数
【分析】注意right join
select d.deptno, d.dname, d.loc, e.num
from (select deptno,count(ename) as num from emp group by deptno) as e
right join dept as d
on e.deptno = d.deptno;
⭐️列出各种工作的最低工资及从事此工作的雇员姓名
【分析】子查询中要查出需要的值,如job
select e2.job,e1.ename, e2.minsal
from emp as e1
join
(select job, min(sal) as minsal
from emp
group by job) as e2
on e1.sal = e2.minsal and e1.job = e2.job;
⭐️列出各个部门的 MANAGER( 领导) 的最低薪金
【分析】where是在group by之前的,也就是先筛选再聚合计算min
select deptno, min(sal)
from emp
where job = 'MANAGER'
group by deptno;
列出所有员工的 年工资, 按 年薪从低到高排序
select ename,(ifnull(comm,0) + sal * 12 ) as yearsal
from emp
order by yearsal;
⭐️求出员工领导的薪水超过3000的员工名称与领导
【分析】很多时候不需要用子查询,特别是join的时候,子查询可能是多余的
方法一:子查询
select e1.ename, e2.ename
from emp as e1
join (select empno,ename from emp where sal > 3000) as e2
on e1.mgr = e2.empno;
方法二:直接用join
select e1.ename, e2.ename
from emp as e1
join emp as e2
on e1.mgr = e2.empno and e2.sal > 3000;
求出部门名称中, 带’S’字符的部门员工的工资合计、部门人数
select d.dname,ifnull(sum(e.sal),0),count(e.ename)
from emp as e
right join dept as d
on e.deptno = d.deptno
where d.dname like '%S%'
group by d.deptno ;
⭐️给任职日期超过 30 年的员工加薪 10%.
【分析】
update emp set sal = sal * 1.1 where TimeStampDiff(YEAR, hiredate, now()) > 30;
insert into 表名(字段名1,字段名2) values (值1,值2)
ex: insert into t_student (no, name) values(1, ‘lisi’);
插入多条记录
insert into t_user(id , name, create_time) values
(1,‘zs’,‘1980-10-11’),
(2,‘as’,‘1990-1-12’)
将查询结果插入到表中
insert intop dept_bak select * from dept
格式化数字 format(数字,‘格式’);
select ename,format(sal, ‘$999,999’) as sal from emp
±-------±------+
| ename | sal |
±-------±------+
| SMITH | 800 |
| ALLEN | 1,600 |
| WARD | 1,250 |
| JONES | 2,975 |
将字符串转化成日期,通常使用在插入过程:str_to_date(‘字符串日期’,‘日期格式’)
ex:
create table t_user(
id int,
name varchar(32),
birth date
);
下向表中插入数据
insert into t_user(id,name,birth) values(1,‘zhangsan’,str_to_date(‘01-10-1990’,‘%d-%m-%Y’));
或
insert into t_user(id,name,birth) values(1,‘zhangsan’,1990-10-01);
将日期格式化成字符串,常用于select中,设置展示日期的格式:date_format
select id, name, date_format(birth, ‘%m/%d/%Y’) as birth from t_user;
date和datetime的区别
date只包括年月日
datetime包括年月日时分秒,默认格式是==%Y-%m-%d %h:%i:%s==
获取系统当前时间 now()
update 表名 set 字段名1=值1,字段名2=值2 where 条件
update t_user set name = ‘jack’,birth=‘2000-10-11’ where id = 1;
delete from 表名 where 条件
delete from t_user where id = 2;
delete from t_user // 删除所有
快速删除表中数据
delete删除只是删除表中数据,但是数据在硬盘中的存储空间不会被释放,但是支持回滚
如果想快速将空间释放,且不需要回滚,则用:
truncate table 表名
(注意区分delete、truncate和drop的区别)
格式
create table 表名(
字段名1 数据类型 default 默认值,
字段名2 数据类型
);
数据类型
ex:t_movie 电影表(专门存储电影)
create table t_movie (
no bigint,
name varchar,
description clob,
playtime date,
time double,
image blob,
type char
)
快速创建表(复制表,把查询结果当做一张表新建出来)
create table emp2 as select * from emp
create table mytable as select empno,ename from emp where job = ‘MANAGER’;
定义:创建表时,给表中字段加上约束,来保证表中数据的完整性、有效性
约束分类:
非空约束
有not null约束的字段不能为NULL
ex:drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) not null
);
insert into t_vip(id, name) values(1,‘zhangsan’);
insert into t_vip(id) values(3);(这个字段有问题)
唯一性约束 unique
ex:要求name和email两个字段联合起来具有唯一性(即表级约束)
drop table if_exists t_vip;
create table t_vip(
id int,
name varchar(255),
email varchar(255),
unique (name, email)
);
主键约束(primary key,简称pk)
如果一个字段同时not null和unique,这个字段就变成主键字段了。
主键的特征:not null + unique
主键值是每一行记录的唯一标识
任何一张表都应该有主键
ex:添加主键约束
drop table if exists t_vip;
create table t_vip(
id int primary key,
name varchar(255)
)
ex:添加复合主键
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255),
primary key(id,name)
)
实际开发中不建议使用复合主键
主键约束只能有一个
主键值建议使用int ,bigint,char,不建议用varchar
主键除了单一主键、复合主键还可以这样分类:
实际开发中尽量使用“自然主键”,当业务主键发生变化时不受影响
mysql中有一种机制可以帮助我们自动维护一个主键值:
ex:自动生成主键值
drop table if exists t_vip;
create table t_vip(
id int primary key auto_increment,
name varchar(255)
)
insert into t_vip(name) values(‘zhangsan’);
insert into t_vip(name) values(‘zhangsan’);
这时会自动生成id的值,第一个zhangsan的id为1,第二个zhangsan的id为2
外键约束(foreign key,简称fk)
业务背景:设计数据库表来描述班级和学生的信息
第一种方式:班级和学生存在一张表中
no(pk) name classno classname
这种方式不好,会产生冗余
第二种方式:班级一张,学生一张表
t_class:classno(pk) classname
t_student: no(pk) name cno
cno就是classno,为了保证cno中的字段在classno中都存在,所以为cno加外键约束
drop table if exists t_student;
drop table if exists t_class;
create table t_classs(
classno int primary key,
classname varchar(255)
);
create table t_student(
no int primary key auto_increment,
name varchar(255),
cno int,
foreign key(cno) references t_class(classno)
)
注意这里先删除子表,后删除父表;先创建父表,再创建子表
很少用到,这里就不学了
什么是存储引擎
是mysql中特有的,是一个表存储数据的方式。不同的存储引擎,表存储数据的方式不同。
如何添加存储引擎
mysql默认的存储引擎是InnoDB
ex: create table t_product(
id int primary key,
name varchar(255)
) engine = InnoDB default charset=gbk;
查看mysql支持哪些存储引擎
show engines \G
版本不同支持情况不同
mysql常用的存储引擎
MyISAM:
使用三个文件表示每个表
可被转换为压缩、只读表来节省空间
InnoDB(默认是存储引擎,重量级的)
MEMORY
一个事务=一个完整的业务逻辑=最小的工作单元
ex:从A账务向B账户中转账10000。则将A账户中减去10000,将B账户中加上10000,这就是一个完整的业务逻辑,是一个最小的工作单元,不能只完成一半
只要操作涉及数据的增删改,就一定要考虑事务(即最小工作单元)
因为做某件事需要多条dml语句联合起来才能完成,所以需要事务存在。如果任何一个复杂的事都可以用一条dml语句完成,就不需要事务的概念了。一个事务的本质就是多条dml语句同时成功,或同时失效。
InnoDB存储引擎提供一组用来记录事务性活动的日志文件,在事务执行过程中,每一条dml操作都会记录到事务性活动的日志文件中。
在事务执行过程中,我们可以提交事务,也可以回滚事务。
提交事务时会清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中,提交事务标志着,事务的结束,并且是一种全部成功的结束。
回滚事务时会将之前所有的dml操作全部撤销,并且清空事务性活动的日志文件。回滚事务标志着事务的结束,并且是一种全部失败的结束。
4个隔离级别
读未提交:read uncommitted,事务A可以读取事务B未提交的数据,这种现象被称为“脏读”
读已提交:read committed,事务A只能读到事务B提交的数据,这种现象解决了脏读,但是不可重复读取数据,每次读取的数据都是当前数据库中的真实存在的数据,数据是随着其他事务提交而变化的
解决了“脏读”问题
oracle默认的隔离级别
可重复读:repeatable read,在事务A开启之后,不管是多久,每一次事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变。
解决了“不可重复读”的问题
存在“幻影读”的问题,读取到的数据不够真实。
mysql中默认的事务隔离级别
序列化/串行化:serializable(最高的隔离级别),事务不能同时执行
查看隔离级别
select @@tx_isolation;
设置隔离级别
set global transaction isolation level read uncommited;
t_user表:
id name(nameIndex) email address
1 zhangsan…
2 lisi
3 wangwu
4 zhaoliu
5 hanmeimei
6 jack
select * from t_user where name = ‘jack’;
以上的这条SQL语句会去name字段上扫描,为什么?
因为查询条件是:name=‘jack’
如果name字段上没有添加索引(目录),或者说没有给name字段创建索引, MySQL会进行全扫描,会将name字段上的每一个值都比对一遍。效率比较低。
如果name字段添加了索引(目录)。在mysql数据库当中索引是需要排序的,并且这个索引的排序和TreeSet
数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树,即在mysql当中索引是一个B-Tree数据结构,遵循左小又大原则存放,采用中序遍历方式遍历取数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GEYqz82O-1672670259184)(C:\Users\junlines\AppData\Roaming\Typora\typora-user-images\image-20230102004443197.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T0Itce7i-1672670259185)(C:\Users\junlines\AppData\Roaming\Typora\typora-user-images\image-20230102004745062.png)]
条件1:数据量庞大
条件2:该字段经常出现在where后面,即这个字段总是被扫描
条件3:该字段很少的DML操作(因为DML之后,该字段的索引需要重新排序
建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。
建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的。
创建索引
create index emp_ename_index on emp(ename);
给emp表的ename字段添加索引,起名:emp_ename_index
删除索引
drop index emp_ename_index on emp;
将emp表上的emp_ename_index索引对象删除。
查看索引
explain select * from emp where ename = ‘KING’;
ex:select * from emp where ename like ‘%T’;
此时失效,因为模糊匹配中以%开头,所以模糊查询尽量避免用%开始,这是一种优化的策略
ex:使用or的时候,必须两边都要有索引
尽量少用or
ex:使用复合索引的时候没有使用左侧的列查找
ex:在where中索引列参加了数学运算
ex:在where中索引列使用了函数
单一、复合、主键、唯一性(在有unique字段上添加的索引)
view:站在不同的角度去看待同一份数据。
表复制: create table dept2 as select * from dept;
创建视图对象:create view dept2_view as select * from dept2;
删除视图对象:drop view dept2_view;
注意:只有DQL语句才能以view的形式创建。
我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作(视图最大特点)
假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用。
每一次使用这个sql语句的时候都需要重新编写,很长,很麻烦,怎么办?
可以把这条复杂的SQL语句以视图对象的形式创建,在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发。
并且利于后期的维护,因为修改的时候也只需要修改一个位置就行,只需要修改视图对象所映射的SQL语句。
提示:视图对象也是存储在硬盘上的,而不是只在内存中,断点不会消失
程序员重点掌握数据的导入和导出
导出:mysqldump 数据库名称 (表名)>filename.sql -uroot -p123456
导入:创建数据库-> 使用数据库->初始化数据库:source filename.sql
什么是数据库设计范式?
怎么进行数据库的设计,按照三范式设计可以避免表中数据冗余、空间浪费
最核心,最重要的范式,所有表的设计都需要满足。
学生编号 学生姓名 联系方式
1001 张三 [email protected],1359999999
1002 李四 [email protected],13699999999
1001 王五 [email protected],13488888888
以上是学生表,满足第一范式吗?
不满足,第一:没有主键。第二:联系方式可以分为邮箱地址和电话
学生编号(pk) 学生姓名 邮箱地址 联系电话
1001 张三 [email protected] 1359999999
1002 李四 [email protected] 13699999999
1003 王五 [email protected] 13488888888
学生编号+教师编号(pk) 学生姓名 教师姓名
1001 001 张三 王老师
1002 002 李四 赵老师
1003 001 王五 王老师
1001 002 张三 赵老师
学生编号 教师编号,两个字段联合做主键,复合主键(PK: 学生编号+教师编号)
以上的表满足了第一范式。但是满足第二范式吗?
不满足,“张三”依赖1001,“王老师”依赖001,显然产生了部分依赖。
产生部分依赖有什么缺点?
数据冗余了。空间浪费了。“张三”重复了,“王老师”重复了。
为了让以上的表满足第二范式,你需要这样设计:
使用三张表来表示多对多的关系!!!!
背口诀:
多对多怎么设计?
多对多,三张表,关系表两个外键!!!!
学生表:
学生编号(pk) 学生名字
1001 张三
1002 李四
1003 王五
教师表:
教师编号(pk) 教师姓名
001 王老师
002 赵老师
学生教师关系表:
id(pk) 学生编号(fk) 教师编号(fk)
1 1001 001
2 1002 002
3 1003 001
4 1001 002
学生编号(PK) 学生姓名 班级编号 班级名称
1001 张三 01 一年一班
1002 李四 02 一年二班
1003 王五 03 一年三班
1004 赵六 03 一年三班
以上表的设计是描述:班级和学生的关系。很显然是1对多关系!
一个教室中有多个学生。
分析以上表是否满足第一范式?
满足第一范式,有主键。
分析以上表是否满足第二范式?
满足第二范式,因为主键不是复合主键,没有产生部分依赖。主键是单一主键。
分析以上表是否满足第三范式?
第三范式要求:不要产生传递依赖!
一年一班依赖01,01依赖1001,产生了传递依赖。
不符合第三范式的要求。产生了数据的冗余。
那么应该怎么设计一对多呢?
一对多,两张表,多的表加外键!!!!
班级表:一
班级编号(pk) 班级名称
01 一年一班
02 一年二班
03 一年三班
学生表:多
学生编号(PK) 学生姓名 班级编号(fk)
1001 张三 01
1002 李四 02
1003 王五 03
1004 赵六 03
05
MN080{D.png)duplicate entry 。。。。。。(完全一样的记录)
字段添加了唯一性约束,而当前语句破坏了唯一性约束