SQL语言及Mysql基本知识
本文是以下课程的课程笔记:MySQL入门基础,mysql基础视频+数据库实战,老杜带你学
重点在于SQL查询语言,还需理解存储引擎、事务、索引、视图、数据库设计三范式等内容,面试可能会问到。
mysql workbanch
可略过
基本用法介绍
安装、重要文件介绍
https://zhuanlan.zhihu.com/p/188416607
https://blog.csdn.net/liuzuoping/article/details/101931559 创建了配置文件
- 修改密码 mysqladmin -u用户名 -p旧密码 password 新密码
预备操作命令
# 启动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
sql语言
☆DQL:数据查询语言
简单查询
- 查看整个表:select * from [tablename];
- 查看表结构,不看数据:desc [tablename];
- 查看当前用的是哪个数据库:select database();
- 查看数据库版本:select version();
- 查看事务隔离级别:select @@tx_isolation;
- 查询表中字段:select 字段1,字段2 from 表
- (实际使用时不要用select * 因为效率低)
- 给查询的列起别名:select deptno,dname as deptname from dept; 或者 select deptno,dname ‘deptname‘ from dept;
- (只是将显示出来的查询结果列名更改,不会改表中的实际列名)
- 查询年薪(表中只给了月薪):select ename, sal * 12 as ’年薪’ from emp;
- 字段值可以参与运算
条件查询
-
格式: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之前执行
☆分组查询 group by、having
写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语句的顺序:
- from
- where
- group by
- having
- select
- order by
-
ex:计算每个工作岗位的工资和
select job,sum(sal) from emp group by job
执行过程:
- 先从emp表中查询数据
- 根据job字段进行分组
- 然后对每一组的数据进行sum(sal)
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表中取出部门名字,这种跨表查询、多张表联合起来查询数据,被称为 连接查询
-
分类:
-
根据语法的年代分类:
- SQL92:1992年推出的语法
- SQL99:1999年推出的语法(我们学这个)
-
根据表连接的方式分类:
-
内连接
- 等值连接
- 非等值连接
- 自连接
-
外连接
-
左外连接(左连接)
-
右外连接(右连接)
-
全连接(不讲)
-
-
-
-
笛卡尔积:两张表没有任何限制地连接时,表一的每一个字段和表二的所有字段都产生连接,总共产生的条目数是表一条目数 * 表二条目数
-
怎么避免笛卡尔积:表连接时加条件
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;
☆子查询:难想,写sql前先定思路
-
定义: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
-
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要求两个结果集列数相同、数据类型相同
☆取出部分查询结构——limit 起始下标,取出长度
-
通常使用在分页查询中显示,为了提高用户体验
-
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;
34道经典select题
加⭐️是我不会做的
-
⭐️取得每个部门最高薪水的人员名称
【分析】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;
-
⭐️取得部门中(所有人的)平均的薪水等级
【分析】
- 错在审题错误,是所有人的平均的薪水等级,不是“所有人的平均薪水的等级”
- 第一步:取得所有人的薪水等级,用连接查询
- 第二步:算平均值,用group by
【解】
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 ),取得最高薪水
【分析】
- 方法1:用order by 和limit
- ☆没有想起来的方法2:表的自连接,用not in
【解】
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);
-
取得平均薪水最高的部门的部门编号
【分析】
- 取得平均薪水,用group by
- 排序,取出第一个
- 方法二:用max来做,比较麻烦,这里不写了
【解】
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;
-
求平均薪水的等级最低的部门的部门名称
【分析】
- 找出部门的平均薪水,用group by
- 找出部门平均薪水的等级最低的部门,用表连接、order by 和limit
- 找出部门名称,用表连接
【解】
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 字段上出现的) 的最高薪水还要高的领导人姓名
【分析】
- 找出普通员工的最高薪水,用where、group by 和 max(或者order by 和 limit)
- 找出比最高薪水还高的领导人的姓名,用表连接
- 错在not in在使用的时候,后面小括号中记得排除NULL。
【解】
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;
-
取得每个薪水等级有多少员工
【分析】
- 取得每个员工的薪水等级,用表连接
- 算每个薪水等级员工个数,用count函数和group by
【解】
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 个员工的所有部门
【分析】
- 算出部门员工人数,用count函数和group by
- 找出至少5个员工的部门,用where
- 想了一会,不可以只用一个select,没想到用having来给group by后的结果筛选
【解】
方法一:
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 表名 set 字段名1=值1,字段名2=值2 where 条件
- TimeStampDiff(YEAR, hiredate, now());
update emp set sal = sal * 1.1 where TimeStampDiff(YEAR, hiredate, now()) > 30;
DML:数据操纵语言(操作表中数据)
- 增 insert
- 删 delete
- 改 update
增 insert
-
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(‘字符串日期’,‘日期格式’)
- 日期格式:%Y年 %m月 %d日 %h时 %i分 %s秒
- 如果提供的日期字符串恰好是 %Y-%m-%d就可以自动转换,不需要使用这个函数
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
-
update 表名 set 字段名1=值1,字段名2=值2 where 条件
update t_user set name = ‘jack’,birth=‘2000-10-11’ where id = 1;
删 delete
-
delete from 表名 where 条件
delete from t_user where id = 2;
delete from t_user // 删除所有
-
快速删除表中数据
delete删除只是删除表中数据,但是数据在硬盘中的存储空间不会被释放,但是支持回滚
如果想快速将空间释放,且不需要回滚,则用:
truncate table 表名
(注意区分delete、truncate和drop的区别)
DDL:数据定义语言(操作表的结构)
- 新建表:create [tableName];
- 删除:drop [tableName];
- 修改:alter
- 导入文件:source [fileName];
建表 create
-
格式
create table 表名(
字段名1 数据类型 default 默认值,
字段名2 数据类型
);
-
数据类型
- varchar:可变长度字符串,根据传过来的数据长度动态分配空间,节省空间
- char:定长字符串,与varchar相对,效率高
- int
- bigint:等用于long
- float
- double
- date:短日期类型
- datetime:长日期类型
- clob:字符大对象,超过255个字符的都用他,最多存储4G的字符串,如一个简介,文章
- blob:二进制大对象,存储图片、声音、视频等流媒体数据,插入数据时需要用IO流
-
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
- 唯一性:unique
- 主键:primary key(简称pk)
- 外键:foreign key(简称fk)
- 检查:check(mysql不支持,oracle支持)
-
非空约束
有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)
)
注意这里先删除子表,后删除父表;先创建父表,再创建子表
-
删除表 drop
- drop table 表名 if exists;
修改表 alter
很少用到,这里就不学了
TCL:事务控制语言(Transactional control language)
- 事务提交:commit;
- 事务回滚:rollback;
DCL:数据控制语言
- 授权:grant
- 撤销权限:revoke
存储引擎(理解)
-
什么是存储引擎
是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:
-
使用三个文件表示每个表
- 格式文件:存储表结构的定义
- 数据文件:存储表行的内容
- 索引文件:存储表上索引(相当于一本书的目录,缩小扫描范围,一张表的主键或者有unique约束的字段都有索引)
-
可被转换为压缩、只读表来节省空间
-
-
InnoDB(默认是存储引擎,重量级的)
- 每个InnoDB表在数据库目录中以==.frm格式文件==表示
- InnoDB表空间 tablespace被用于存储表的内容
- 提供一组用来记录事务性活动的日志文件
- 特点:支持事务,非常安全
- 用COMMIT(提交),SAVEPOINT及ROLLBACK(回滚)支持事务处理
- 提供全ACID兼容
- 在mysql服务器崩溃后提供自动恢复
- 多版本(MVCC)和行级锁定
- 支持外键及引用的完整性,包括级联删除和更新
-
MEMORY
- 数据存在内存中,行的长度固定
- 在数据库目录内,每个表用.frm格式的文件表示
- 表数据和索引被存在内存中
- 不能包含text和blob字段
- 特点:数据断点后丢失,速度快
- 数据存在内存中,行的长度固定
-
☆事务
定义
一个事务=一个完整的业务逻辑=最小的工作单元
ex:从A账务向B账户中转账10000。则将A账户中减去10000,将B账户中加上10000,这就是一个完整的业务逻辑,是一个最小的工作单元,不能只完成一半
只有DML语句才与事务有关系(update\delete\insert)
只要操作涉及数据的增删改,就一定要考虑事务(即最小工作单元)
为什么需要事务
因为做某件事需要多条dml语句联合起来才能完成,所以需要事务存在。如果任何一个复杂的事都可以用一条dml语句完成,就不需要事务的概念了。一个事务的本质就是多条dml语句同时成功,或同时失效。
事务是如何做到多条dml语句同时成功或失效的
InnoDB存储引擎提供一组用来记录事务性活动的日志文件,在事务执行过程中,每一条dml操作都会记录到事务性活动的日志文件中。
在事务执行过程中,我们可以提交事务,也可以回滚事务。
提交事务时会清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中,提交事务标志着,事务的结束,并且是一种全部成功的结束。
回滚事务时会将之前所有的dml操作全部撤销,并且清空事务性活动的日志文件。回滚事务标志着事务的结束,并且是一种全部失败的结束。
如何提交事务、回滚事务
- start transaction :关闭默认提交机制。mysql的默认提交机制是“每执行一条语句就执行一个commit”,而回滚只能回滚到上一次commit时,这种自动提交不符合我们的开发习惯。
- commit
- rollback:回滚到上一次commit后
事务的4个特性ACID
- 原子性:一个事务是最小的工作单元,不可再分
- 一致性:在同一事务当中,所有操作必须同时成功,以保证数据一致性
- 隔离性:A事务和B事务之间具有一定的隔离。
- 持久性:事务提交,就将没有保存到硬盘上的数据保存到硬盘上
隔离性
-
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)]
- 提醒1:在任何数据库当中主键上都会自动添加索引对象,id字段上自动有索引,因为id是PK。另外在mysql当中,一个字段上如果有unique约束的话,也会自动创建索引对象。
- 提醒2:在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。
- 提醒3:在mysql当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在,在MyISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中索引存储在一个逻辑名称叫做tablespace的当中。在MEMORY存储引擎当中索引被存储在内存当中。不管索引存储在哪里,索引在mysql当中都是一个树的形式存在。(自平衡二叉树:B-Tree)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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语句。
提示:视图对象也是存储在硬盘上的,而不是只在内存中,断点不会消失
DBA常用命令
程序员重点掌握数据的导入和导出
导出:mysqldump 数据库名称 (表名)>filename.sql -uroot -p123456
导入:创建数据库-> 使用数据库->初始化数据库:source filename.sql
数据库设计三范式
-
什么是数据库设计范式?
怎么进行数据库的设计,按照三范式设计可以避免表中数据冗余、空间浪费
范式1:必须有主键,每个字段原子性不可再分
最核心,最重要的范式,所有表的设计都需要满足。
学生编号 学生姓名 联系方式
1001 张三 zs@gmail.com,1359999999
1002 李四 ls@gmail.com,13699999999
1001 王五 ww@163.net,13488888888
以上是学生表,满足第一范式吗?
不满足,第一:没有主键。第二:联系方式可以分为邮箱地址和电话
学生编号(pk) 学生姓名 邮箱地址 联系电话
1001 张三 zs@gmail.com 1359999999
1002 李四 ls@gmail.com 13699999999
1003 王五 ww@163.net 13488888888
范式2: 在范式1的基础上,要求所有非主键字段完全依赖主键,不要产生部分依赖
学生编号+教师编号(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
范式3:在范式2的基础上,要求所有非主键字段直接依赖主键,不要产生传递依赖
学生编号(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
总结表的设计方法
- 一对多:两张表,多的表加外键!!
- 多对多:三张表,关系表两个外键!!
- 一对一:外键唯一!!
![img](file:///C:\Users\junlines\AppData\Roaming\Tencent\Users\599351426\QQ\WinTemp\RichOle\RVTYSHWRXS2E05
MN080{D.png)
- 补充:数据库设计三范式是理论上的,实际开发中可能会用冗余换速度,最终的目的是满足用户的需求
常见报错
-
duplicate entry 。。。。。。(完全一样的记录)
字段添加了唯一性约束,而当前语句破坏了唯一性约束