视频链接:老杜带你学_mysql入门基础(mysql基础视频+数据库实战)_哔哩哔哩_bilibili
01-13
一、MySQL常用命令:
0. 登录mysql: mysql -u root -p
1. 退出mysql: exit
2. 查看mysql中有哪些数据库: show databases;(注意:以分号结尾)
3. 如何选择使用某个数据库: use test; 表示正在使用test数据库
4. 如何创建数据库: create database bjpowernode;
5. 查看指定数据库下有没有表:
use bjpowernode;
show tables;
6. 如何将sql文件中的数据导入?
source D:\Mysql_example\bjpowernode.sql 注意: 路径中不要有中文
7. 如何查看表中的数据?
select * from 表名; // 统一执行这个SQL语句
8. 不看表中的数据,只看表的结构
desc 表名;
9. 查看mysql数据库的版本号
select version();
10. 查看当前使用的是哪个数据库?
select database();
11. 不见分号;不执行
12. \c 用来终止一条命令的输入
二、SQL语句的分类
DQL: 数据查询语言 select...
DML: 数据操作语言 insert 增 delete 删 update 改
这个主要是操作表中的数据
DDL:数据定义语言 create/drop/alter 增/删/改
这个增删改和DML不同,这个主要是对表结构进行操作。
TCL:事务控制语言:包括事务提交: commit; 事务回滚: rollback;
DCL: 数据控制语言 如:授权grant 撤销权限revoke......
14 - 25
一、简单查询
1. 查询一个字段?
select 字段名 from 表名;
其中,select和from都是关键字;字段名和表名都是标识符。
2. 查询两个字段或多个字段?
select 字段名1,字段名2,... from 表名 // 字段名用逗号隔开
3. 查询所有字段
// 方式一: 所有字段名都写上,用逗号隔开
select 字段1,字段2,... from 表名
// 方式二: 使用*代替字段名
select * from 表名 // 效率低、可读性差、实际开发中不建议
4. 给查询的列起别名
select dname as deptname from dept; // 使用as关键字起别名
注意:只是将显示的查询结果列名,显示为deptname,原表列名不变
select语句只负责查询,不进行修改
as 关键字可以省略
假设起别名时,别名中有空格,如何处理?
select depton,dname 'dept name' from dept; // 单引号括上
select depton,dname "dept name" from dept; // 双引号括上
注意: 在所有的数据库当中,字符串统一使用单引号括起来,单引号是标准。双引号在oracle数据库中无法使用,在MySQL中可以使用。
5. 计算员工年薪? sal*12
desc emp; // 查看emp表结构
select ename,sal from emp; // 查询ename和sal字段
+--------+---------+
| ename | sal |
+--------+---------+
| SMITH | 800.00 |
| ALLEN | 1600.00 |
| WARD | 1250.00 |
| JONES | 2975.00 |
| MARTIN | 1250.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
| TURNER | 1500.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| FORD | 3000.00 |
| MILLER | 1300.00 |
+--------+---------+
// sal * 12 字段可以使用数学表达式!
select ename,sal*12 from emp;
+--------+----------+
| ename | sal*12 |
+--------+----------+
| SMITH | 9600.00 |
| ALLEN | 19200.00 |
| WARD | 15000.00 |
| JONES | 35700.00 |
| MARTIN | 15000.00 |
| BLAKE | 34200.00 |
| CLARK | 29400.00 |
| SCOTT | 36000.00 |
| KING | 60000.00 |
| TURNER | 18000.00 |
| ADAMS | 13200.00 |
| JAMES | 11400.00 |
| FORD | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
// sal*12 起别名:yearsal
select ename,sal*12 as yearsal from emp;
// 若别名是中文,用单引号括起来
select ename,sal*12 as '年薪' from emp;
+--------+----------+
| ename | yearsal |
+--------+----------+
| SMITH | 9600.00 |
| ALLEN | 19200.00 |
| WARD | 15000.00 |
| JONES | 35700.00 |
| MARTIN | 15000.00 |
| BLAKE | 34200.00 |
| CLARK | 29400.00 |
| SCOTT | 36000.00 |
| KING | 60000.00 |
| TURNER | 18000.00 |
| ADAMS | 13200.00 |
| JAMES | 11400.00 |
| FORD | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
二、条件查询
select 字段1,字段2,... from 表名 where 条件; // 语法格式
1. 查询薪资等于800的员工姓名和编号? =
select empno,ename from emp where sal = 800;
1.1 查询SMITH的编号和薪资?
select empno,sal from emp where ename = 'SMITH'; // 字符串使用单引号
2. 查询薪资不等于800的员工姓名和编号? <> 或 !=
select empno,ename from emp where sal != 800;
select empno,ename from emp where sal <> 800;
3. 查询薪资小于等于2000的员工姓名和编号? <=
select empno,ename from emp where sal <= 2000;
4. 查询薪资大于2000的员工姓名和编号? >
select empno,ename from emp where sal > 2000;
5. 查询薪资在2450和3000之间的员工信息?(包括边界)
between ... and ... 两个值之间, 等同于 >= and <=
select empno,ename,sal from emp where sal >= 2450 and <= 3000;
select empno,ename,sal from emp where sal between 2450 and 3000;
注意: 使用between...and... 的时候,必须保证左小、右大,且是闭区间。
6. 查询哪些员工的津贴/补助为null? is null
select empno,ename,sal,comm from emp where comm is null; // 不能用=号判断null
7. 查询哪些员工的津贴/补助不为null? is not null
select empno,ename,sal,comm from emp where comm is not null;
8. 查询工作岗位是MANAGER并且工资大于2500的员工信息? and
select empno,ename,job,sal from emp where job = 'MANAGER' and sal > 2500;
9. 查询工作岗位是MANAGER或者工作岗位是SALESMAN的员工? or
select empno,ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
10. and 和 or 同时出现,有优先级问题吗? --- and 优先级高于 or
查询工资大于2500,并且部门编号为10或20部门的员工?
select * from emp where sal > 2500 and (deptno = 10 or deptno = 20); // 加括号,提高or的优先级
11. in: 包含, 相当于多个or (not in: 不包含)
查询工作岗位是MANAGER或者工作岗位是SALESMAN的员工?
select empno,ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
select empno,ename,job from emp where job in('MANAGER','SALESMAN');
注意:in不是一个区间。in后面跟的是具体的值。
查询薪资不是800和5000,3000的员工信息?
// not in 表示不在这几个值当中的数据
select ename,sal from emp where sal not in(800,5000,3000);
not 可以取非,主要用在 is 或 in:is null, is not null; in, not in;
三、 模糊查询
like,称为模糊查询,支持%或下划线_匹配;%匹配任意多个字符,下划线_匹配任意一个字符。
1. 查询名字中含有O的?
select ename from emp where ename like '%O%';
2. 查询名字以T结尾的?
select ename from emp where ename like '%T';
3. 查询名字以K开头的?
select ename from emp where ename like 'K%';
4. 查询第二个字母是A的?
select ename from emp where ename like '_A%';
5. 查询第三个字母是R的?
select ename from emp where ename like '__R%';
6. 查询名字中有"_"的?
select ename from emp where ename like '%\_%'; // 通过转义符\,'%_%'不可行
25 - 36
一、排序
1. 查询所有员工薪资,排序?
select ename,sal from emp order by sal; // 默认升序
select ename,sal from emp order by sal asc; // 指定升序排序
select ename,sal from emp order by sal desc; // 指定降序排序
2. 查询员工名字和薪资,先按照薪资升序排,若薪资一致,再按名字升序排?
select ename,sal from emp order by sal asc,ename asc;
// sal 在前,起主导;只有sal相等的情况下,才启用ename排序
3. (了解) 根据查询字段的位置排序?
select ename,sal from emp order by 2; // 2表示第二列。第二列是sal
// 按照查询结果的第二列排序,即按照sal排序
// 实际开发中不建议这样写
二、综合案例
1. 查询工资在1250~3000之间的员工信息,要求按薪资降序排列?
select ename,sal from emp where sal between 1250 and 3000 order by sal desc;
// 该语句的执行顺序:from → where → select → order by(排序总是在最后执行!)
三、单行处理函数
1. 单行处理函数的特点
单行处理函数的特点:一个输入对应一个输出;
多行处理函数的特点:多个输入对应一个输出;
2. 常见的单行处理函数?
2.1 lower
select lower(ename) as ename from emp; // lower 转换小写
2.2 upper
select upper(ename) as ename from emp; // upper 转换大写
2.3 substr
substr:取子串 格式:substr(被截取的字符串,起始下标,截取的长度)
注意:起始下标从1开始,没有0
查询员工名字第一个字母是A的员工信息?
// 方式一: 模糊查询
select ename from emp where ename like 'A%';
// 方式二: substr函数
select ename from emp where substr(ename,1,1) = 'A';
2.4 contact:进行字符串的拼接
select contact(empno,ename) from emp; // 拼接empno,ename
2.5 length 取长度
select length(ename) as enamelength from emp;
2.6 trim 去空格
select * from emp where ename = trim(' KING'); // trim 去前后空白
2.7 round 四舍五入
注意: select 后面可以跟某个表的字段名,也可以跟字面量/字面值
select round(1236.567,0) as result from emp; // 0表示保留小数的位数
// 1 就是保留1位小数...
// -1 1240
2.8 rand() 生成随机数
select rand() from emp;
2.9 ifnull
注意: NULL只要参与运算,最终结果一定是NULL。为了避免这个现象,需要使用ifnull函数。
ifnull函数用法: ifnull(数据,新值) 若数据为NULL,用新值代替;否则不变。
// 计算年薪
select ename,(sal + ifnull(comm,0))*12 as yearsal from emp;
// 补助为NULL时,用0代替
2.10 case..when..then..when..then..else..end
当员工的岗位是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;
37 - 46
一、分组函数/多行处理函数
注意: 分组函数在使用的时候必须先进行分组,然后才能使用;若没分组,则认为整张表是一组数据。
1. count: 计数;sum: 求和;avg: 求平均;max: 最大值;min: 最小值;
注意:select在group by之后执行,所以默认分为一组后,再执行select。
1.1 查询最高工资
select max(sal) from emp;
1.2 查询最低工资
select min(sal) from emp;
1.3 计算工资和
select sum(sal) from emp;
1.4 计算平均工资
select avg(sal) from emp;
1.5 计算员工数量
select count(ename) from emp;
2. 分组函数在使用的时候需要注意哪些?
第一点:分组函数自动忽略NULL,不需要提前对NULL进行处理;
第二点:分组函数中count(*) 和 count(具体字段) 有什么区别?
count(具体字段):表示统计该字段下所有不为NULL的元素的总数;
count(*):统计表当中的总行数;
第三点:分组函数不能直接使用在where子句中;
原因: 分组函数在使用的时候必须先分组,where执行时,还未分组(where先于group by 执行)。
查询比最低工资高的员工信息?
第四点:所有的分组函数可以组合起来一起用;
select sum(sal),min(sal),max(sal) from emp;
二、分组查询 (极其重要)
1. 什么是分组查询?
在实际应用中,可能需要先进行分组,然后对每一组的数据进行操作,此时就需要使用分组查询。
// 分组查询格式
select ... from ... group by ...
2. 结合之前的关键字,它们的执行顺序是什么?
select...from...where...group by...order by...;
// 执行顺序: 1.from 2.where 3.group by 4.select 5.order by
3. 查询每个工作岗位的工资和?
// 实现思路: 先按照工作岗位分组,然后对工资求和
select job,sum(sal) from emp group by job;
重要结论:在一条select语句当中,如果有group by 语句的话,select后面只能跟:参加分组的字段,以及分组函数,其他一律不能跟。
4. 查询每个部门的最高薪资?
// 实现思路: 按照部门编号分组,求每一组sal的最大值
select deptno,max(sal) from emp group by depton;
5. 查询每个部门不同工作岗位的最高薪资?
// 技巧: 两个字段联合成一个字段看(两个字段联合分组)
select deptno,job,max(sal) from emp group by deptno,job;
6. 使用 having 可以对分完组之后的数据进一步过滤。
having不能单独使用,having不能代替where,having必须和group by 联合使用。
查询每个部门最高薪资,要求显示最高薪资大于3000的?
// 第一步: 找出每个部门的最高薪资
select deptno,max(sal) from emp group by deptno;
// 第二步: 要求显示最高薪资大于3000
// 使用having可以对分完组后的数据进一步过滤
select deptno,max(sal) from emp group by deptno having max(sal) > 3000;
// 使用where先过滤也可以
select deptno,max(sal) from emp where sal > 3000 group by deptno; // 效率更高
优化策略: where和having,优先选择where,若where完成不了,再使用having。
7. 查询每个部门的平均薪资,要求显示平均薪资高于2500的?
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2500;
// 本题仅能使用having, 无法使用where优化
三、单表查询总结
select...from...where...group by...having...order by...;
// 以上关键字只能按这个顺序,不能颠倒
// 执行顺序: 1.from 2.where 3.group by 4.having 5.select 6.order by
// 从某张表中查询数据;
// 先经过where条件筛选出有价值的数据;
// 对这些有价值的数据进行分组;
// 分组之后可以使用having继续筛选;
// select查询出来;
// 最后排序输出.
查询每个岗sel位的平均薪资,要求显示平均薪资大于1500的,除MANAGER之外,要求按照平均薪资降序排?
select job,avg(sal) as avgsal from emp where job != 'MANAGER' group by job having avg(sal) > 1500 order by avgsal desc;
47 - 56
一、查询结果去除重复记录 - distinct
select distinct job from emp; // 去除job的重复记录
select distinct job,deptno from emp; // job和deptno联合起来去除重复记录
统计一下工作岗位的数量?
select count(distinct job) from emp;
二、连接查询
1. 什么是连接查询?
从一张表中单独查询,称为单表查询;
emp表和dept表联合起来查询数据,从emp表中取员工名字,从dept表中取部门名字。这种跨表查询,多张表联合起来查询数据,被称为连接查询。
2. 连接查询的分类
根据表连接的方式分类:
内连接:等值连接、非等值连接、自连接
外连接:左外连接(左连接)、右外连接(右连接)
全连接(不讲)
3. 当两张表进行连接查询时,没有任何条件的限制会发生什么现象?
3.1 笛卡尔积现象
当两张表进行连接查询,没有任何条件限制的时候,最终查询结果条数,是两张表条数的乘积,这种现象被称为:笛卡尔积现象。
3.2 如何避免笛卡尔积现象?
连接时加条件,满足这个条件的记录被筛选出来
// 起别名能提高效率 SQL92语法
select e.ename,d.dname from emp e,dept d where e.deptno = d.deptno;
思考: 最终查询的结果条数是14条,但是匹配的过程中,匹配的次数减少了吗?
还是14*4 = 56次,只不过进行了四选一。次数没有减少。
注意: 通过笛卡尔积现象得出,表的连接次数越多效率越低。
4. 内连接之等值连接
案例:查询每个员工所在部门名称,显示员工名和部门名?
// emp e 和 dept d 表进行连接,条件是: e.deptno = d.deptno
// SQL92 语法
select e.ename,d.deptno from emp e,dept d where e.deptno = d.deptno;
// SQL92的缺点: 结构不清晰,表的连接条件,和后期进一步筛选的条件,都放到了where后面。
// SQL99 语法
select ... from 表a join 表b on a和b的连接条件 where 筛选条件;
select e.ename,d.deptno from emp e join dept d on e.deptno = d.deptno;
// SQL99优点: 表连接的条件是独立的。连接之后,如果还需要进一步筛选,再往后继续添加where。
// inner 可以省略
select e.ename,d.dname from emp a inner join dept d on e.deptno = d.deptno;
// 条件是等量关系,所以是等值连接
5. 内连接之非等值连接
连接条件不是等量关系,称为非等值连接。
案例:查询每个员工的薪资等级,要求显示员工名、薪资、薪资等级?
// 首先,查看emp、salgrade表中的信息
select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
select * from salgrade;
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+
// 然后,连接两表中的信息,连接条件为e.sal 在s表的losal和hisal范围内
select e.ename,e.sal,s.grade from emp e join salgrade s on e.sal between losal and hisal;
+--------+---------+-------+
| ename | sal | grade |
+--------+---------+-------+
| SMITH | 800.00 | 1 |
| ALLEN | 1600.00 | 3 |
| WARD | 1250.00 | 2 |
| JONES | 2975.00 | 4 |
| MARTIN | 1250.00 | 2 |
| BLAKE | 2850.00 | 4 |
| CLARK | 2450.00 | 4 |
| SCOTT | 3000.00 | 4 |
| KING | 5000.00 | 5 |
| TURNER | 1500.00 | 3 |
| ADAMS | 1100.00 | 1 |
| JAMES | 950.00 | 1 |
| FORD | 3000.00 | 4 |
| MILLER | 1300.00 | 2 |
+--------+---------+-------+
6. 内连接之自连接
案例:查询员工的上级领导,要求显示员工名和对应的领导名?
select empno,ename,mgr from emp;
+-------+--------+------+
| empno | ename | mgr |
+-------+--------+------+
| 7369 | SMITH | 7902 |
| 7499 | ALLEN | 7698 |
| 7521 | WARD | 7698 |
| 7566 | JONES | 7839 |
| 7654 | MARTIN | 7698 |
| 7698 | BLAKE | 7839 |
| 7782 | CLARK | 7839 |
| 7788 | SCOTT | 7566 |
| 7839 | KING | NULL |
| 7844 | TURNER | 7698 |
| 7876 | ADAMS | 7788 |
| 7900 | JAMES | 7698 |
| 7902 | FORD | 7566 |
| 7934 | MILLER | 7782 |
+-------+--------+------+
// SMITH的mgr是7902,7902的名字是FORD
技巧:一张表看成两张表;emp a 员工表 emp b 领导表
select a.ename as '员工',b.ename as '上级' from emp a join emp b on a.mgr = b.empno;
+--------+-------+
| 员工 | 上级 |
+--------+-------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+-------+
57 - 58
7. 外连接
select e.ename,d.dname from emp e right join dept d on e.deptno = d.deptno;
// right代表什么: 表示将join关键字在右边的这张表看成主表,
// 主要是为了将这张表的数据全部查询出来,捎带着关联查询左边的表
// 右连接 改成 左连接
select e.ename,d.dname from dept d left join emp e on e.deptno = d.deptno;
// 带着outer 是可以省略,带着可读性强
select e.ename,d.dname from emp e right outer join dept d on e.deptno = d.deptno;
+--------+------------+
| ename | dname |
+--------+------------+
| CLARK | ACCOUNTING |
| KING | ACCOUNTING |
| MILLER | ACCOUNTING |
| SMITH | RESEARCH |
| JONES | RESEARCH |
| SCOTT | RESEARCH |
| ADAMS | RESEARCH |
| FORD | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| MARTIN | SALES |
| BLAKE | SALES |
| TURNER | SALES |
| JAMES | SALES |
| NULL | OPERATIONS | // d表有这个,a表没有,用NULL匹配
+--------+------------+
带有 right 的是右外连接,又叫做右连接;
带有 left 的是左外连接,又叫做左连接;
任何一个右连接都有左连接的写法;任何一个左连接都有右连接的写法。
思考: 外连接的查询结果条数一定是 >= 内连接的查询结果条数?
正确。
案例: 查询每个员工的上级领导,要求显示所有员工的名字和领导名?
select empno,ename,mgr from emp;
+-------+--------+------+
| empno | ename | mgr |
+-------+--------+------+
| 7369 | SMITH | 7902 |
| 7499 | ALLEN | 7698 |
| 7521 | WARD | 7698 |
| 7566 | JONES | 7839 |
| 7654 | MARTIN | 7698 |
| 7698 | BLAKE | 7839 |
| 7782 | CLARK | 7839 |
| 7788 | SCOTT | 7566 |
| 7839 | KING | NULL | // 要求把这个也显示出来
| 7844 | TURNER | 7698 |
| 7876 | ADAMS | 7788 |
| 7900 | JAMES | 7698 |
| 7902 | FORD | 7566 |
| 7934 | MILLER | 7782 |
+-------+--------+------+
// 内连接: 将一张表当作两张表用 员工表和领导表
select a.ename as '员工名',b.ename as '领导名' from emp a join emp b on a.mgr = b.empno;
+--------+--------+
| 员工名 | 领导名 |
+--------+--------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+--------+
// 外连接
// 想要显示所有的员工名和领导名,员工KING未显示,他没有领导,所以员工表应为主表
select a.ename as '员工名',b.ename as '领导名' from emp a left outer join emp b on a.mgr = b.empno;
+--------+--------+
| 员工名 | 领导名 |
+--------+--------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| KING | NULL | // 外连接有KING
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+--------+
外连接与内连接的区别: 内连接两张表之间没有主次之分,外连接有一个主表;
8. 多张表怎么连接查询?
// 多张表连接语法
select ... from a join b on a和b的连接条件 join c on a和c的连接条件 right join d on a和d的连接条件...
//注意: 一条SQL语句种内连接和外连接可以混合使用。
案例:查询每个员工的部门名称以及工资等级,要求显示员工名、部门名、薪资、薪资等级?
// 分析过程
// 查看员工表
select * from emp; e 从该表中取ename sal
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
// 查看部门表
select * from dept; d 从该表中取dname
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
// 查看薪资等级
select * from salgrade; s 从该表中取grade
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+
// 连接条件分析
e 表与 d 表的连接条件是e.deptno = d.deptno;
e 表与 s 表的连接条件是e.sal between s.losal and s.hisal;
// 核心代码
select e.ename as '员工名',d.dname as '部门名',e.sal as '薪资',s.grade as '薪资等级' from emp e join dept d on e.deptno = d.deptno join salgrade s on e.sal between s.losal and s.hisal;
+--------+------------+---------+----------+
| 员工名 | 部门名 | 薪资 | 薪资等级 |
+--------+------------+---------+----------+
| SMITH | RESEARCH | 800.00 | 1 |
| ALLEN | SALES | 1600.00 | 3 |
| WARD | SALES | 1250.00 | 2 |
| JONES | RESEARCH | 2975.00 | 4 |
| MARTIN | SALES | 1250.00 | 2 |
| BLAKE | SALES | 2850.00 | 4 |
| CLARK | ACCOUNTING | 2450.00 | 4 |
| SCOTT | RESEARCH | 3000.00 | 4 |
| KING | ACCOUNTING | 5000.00 | 5 |
| TURNER | SALES | 1500.00 | 3 |
| ADAMS | RESEARCH | 1100.00 | 1 |
| JAMES | SALES | 950.00 | 1 |
| FORD | RESEARCH | 3000.00 | 4 |
| MILLER | ACCOUNTING | 1300.00 | 2 |
+--------+------------+---------+----------+
14 rows in set (0.00 sec)
案例: 查询每个员工的部门名称以及工资等级,还有上级领导,要求显示员工名、领导名、部门 名、薪资、薪资等级?
注意:KING的MGR为NULL,但KING的领导名要显示出来 --- 用外连接解决,emp表做主表
select e.ename as '员工名',m.ename as '领导名',d.dname as '部门名',e.sal as '薪资',s.grade as '薪资等级' from emp e left join emp m on e.mgr = m.empno join dept d on e.deptno = d.deptno join salgrade s on e.sal between s.losal and s.hisal;
+--------+--------+------------+---------+----------+
| 员工名 | 领导名 | 部门名 | 薪资 | 薪资等级 |
+--------+--------+------------+---------+----------+
| SMITH | FORD | RESEARCH | 800.00 | 1 |
| ALLEN | BLAKE | SALES | 1600.00 | 3 |
| WARD | BLAKE | SALES | 1250.00 | 2 |
| JONES | KING | RESEARCH | 2975.00 | 4 |
| MARTIN | BLAKE | SALES | 1250.00 | 2 |
| BLAKE | KING | SALES | 2850.00 | 4 |
| CLARK | KING | ACCOUNTING | 2450.00 | 4 |
| SCOTT | JONES | RESEARCH | 3000.00 | 4 |
| KING | NULL | ACCOUNTING | 5000.00 | 5 |
| TURNER | BLAKE | SALES | 1500.00 | 3 |
| ADAMS | SCOTT | RESEARCH | 1100.00 | 1 |
| JAMES | BLAKE | SALES | 950.00 | 1 |
| FORD | JONES | RESEARCH | 3000.00 | 4 |
| MILLER | CLARK | ACCOUNTING | 1300.00 | 2 |
+--------+--------+------------+---------+----------+
59 - 62
三、子查询
1. 什么是子查询?
select 语句中嵌套 select 语句,被嵌套的 select 语句称为子查询。
2. 子查询都可以出现在哪里?
select...(select).from...(select).where...(select).;
3. where 子句中的子查询
案例:查询比最低工资高的员工姓名和工资?
// 错误解法
select ename,sal from emp where sal > min(sal);
// 错误原因: 分组函数使用前必须先分组,但where先于默认的group by执行,所以where的子句中不能使用分组函数
// 实现思路:
// 1. 先查询最低工资是多少?
select min(sal) from emp;
+----------+
| min(sal) |
+----------+
| 800.00 |
+----------+
// 2. 再查询比800高的员工姓名和工资?
select ename,sal from emp where sal > 800;
+--------+---------+
| ename | sal |
+--------+---------+
| ALLEN | 1600.00 |
| WARD | 1250.00 |
| JONES | 2975.00 |
| MARTIN | 1250.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
| TURNER | 1500.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| FORD | 3000.00 |
| MILLER | 1300.00 |
+--------+---------+
// 如上的两步可以合成一步, 利用子查询实现
select ename,sal from emp where sal > (select min(sal) from emp);
4. from 子句中的子查询
注意: (技巧) from 后面的子查询,可以将子查询的查询结果当作一张临时表;
案例: 查询每个岗位的平均工资的薪资等级?
// 1. 先查询每个岗位的平均薪资,按照岗位分组来求
select job,avg(sal) from emp group by job;
+-----------+-------------+
| job | avg(sal) |
+-----------+-------------+
| ANALYST | 3000.000000 |
| CLERK | 1037.500000 |
| MANAGER | 2758.333333 |
| PRESIDENT | 5000.000000 |
| SALESMAN | 1400.000000 |
+-----------+-------------+
// 2. 把以上的查询结果当作一张真实存在的表t
// 查看salgrade表;
select * from salgrade; s
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+
// t表与s表进行表连接,条件是t表avg(sal) between s.losal and s.hisal;
select t.job,t.avgsal,s.grade from (select job,avg(sal) as avgsal from emp group by job) t join salgrade s on t.avgsal between s.losal and s.hisal;
// 注意: avg(sal) 要起别名avgsal,否则后面t.avgsal无法使用
+-----------+-------------+-------+
| job | avgsal | grade |
+-----------+-------------+-------+
| CLERK | 1037.500000 | 1 |
| SALESMAN | 1400.000000 | 2 |
| ANALYST | 3000.000000 | 4 |
| MANAGER | 2758.333333 | 4 |
| PRESIDENT | 5000.000000 | 5 |
+-----------+-------------+-------+
5. select 后面的子查询(这个内容不需要掌握,了解即可)
案例: 查询每个员工的部门名称,要求显示员工名、部门名?
select e.ename,(select d.dname from dept d where e.deptno = d.deptno) as dname from emp e;
// 注意: 对于select后面的子查询来说,这个子查询只能返回一条,多于一条就报错
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| JONES | RESEARCH |
| MARTIN | SALES |
| BLAKE | SALES |
| CLARK | ACCOUNTING |
| SCOTT | RESEARCH |
| KING | ACCOUNTING |
| TURNER | SALES |
| ADAMS | RESEARCH |
| JAMES | SALES |
| FORD | RESEARCH |
| MILLER | ACCOUNTING |
+--------+------------+
63 - 70
四、union 合并查询结果集
案例: 查询工资岗位是MANAGER和SALESMAN的员工?
// 写法一
select ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
// 写法二
select ename,job from emp where job in('MANAGER','SALESMAN');
+--------+----------+
| ename | job |
+--------+----------+
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| JONES | MANAGER |
| MARTIN | SALESMAN |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| TURNER | SALESMAN |
+--------+----------+
// 写法三: union 将查询结果合并
select ename,job from emp where job = 'MANAGER'
union
select ename,job from emp where job = 'SALESMAN';
+--------+----------+
| ename | job |
+--------+----------+
| JONES | MANAGER |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| MARTIN | SALESMAN |
| TURNER | SALESMAN |
+--------+----------+
union的效率要高一些,对于表连接来说,每连接一次新表,则匹配的次数满足笛卡尔积,成倍的增长,但是union可以减少匹配的次数。在减少匹配次数的情况下,还可以完成两个结果集的拼接。
a 连接 b 连接 c
a 10条记录; b 10条记录; c 10条记录 匹配次数是:1000
a 连接 b 100次; a 连接 c 100次; union 100 + 100 = 200次 (union 把乘法变成了加法)
union 在使用的时候有什么注意事项?
union 在进行结果集合并时,要求两个结果集的列数相等,结果集合并时,列和列的数据类型也要一致。
五、limit (非常重要)
1. limit 的作用:limit 是将查询结果集的一部分取出来,通常使用在分页查询当中。
例如:百度默认一页显示10条记录,分页的作用是为了提高用户的体验,因为一次全部查出来,用户体验差,可以一页一页翻页看。
2. limit 使用方法:
limit startIndex,length 取起始下标startIndex开始,length为长度
limit length 默认起始下标为0
案例:按照薪资降序,取出排名在前5名的员工?
select ename,sal from emp order by sal desc limit 5;
//
select ename,sal from emp order by sal desc limit 0,5;
+-------+---------+
| ename | sal |
+-------+---------+
| KING | 5000.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
+-------+---------+
3. mysql 中 limit 在 order by 之后执行
案例: 取出工资排名在3-5名的员工?
select ename,sal from emp order by sal desc limit 3,3;
+-------+---------+
| ename | sal |
+-------+---------+
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
+-------+---------+
4. 分页
每页显示3条记录
第 1 页: limit 0,3
第 2 页: limit 3,3
第 3 页: limit 6,3
第 4 页: limit 9,3
每页显示pageSize条记录
第 pageNo 页: limit (pageNo-1)*pageSize,pageSize
记公式: limit (pageNo - 1)*pageSize, pageSize
六、如上,DQL语句已学完,关于DQL语句的总结:
select...from...where...group by...having...order by...limit...;
执行顺序: 1.from 2.where 3.group by 4.having 5.select 6.order by 7.limit
七、表的创建(建表)
1. 建表的语法格式: (建表属于DDL语句,包括create,drop,alter)
create table 表名(字段名1 数据类型,字段名2 数据类型,字段名3 数据类型);
表名:建议以 t_ 或 tbl_ 开始,可读性强;
字段名: 见名知意;
表名和字段名都属于标识符。
2. MySQL中的数据类型?
varchar 可变长度的字符串,会根据实际的数据长度动态分配空间,但速度慢。varchar后面必须指定建议的长度,如varchar(32);
char 定长字符串,不管实际的数据长度是多少,分配固定长度的空间存储数据。使用不恰当时, 可能会导致空间的浪费,但速度快。
varchar 和 char 如何选择?
性别字段:因为性别是固定长度的字符串,所以使用char;
姓名字段:因为每一个人名字长度不同,所以选择varchar;
int 数字中的整型,等同于java的int;
bigint 数字中的长整型,等同于java中的long;
float 单精度浮点型数据
double 双精度浮点型数据
date 短日期类型
datetime 长日期类型
clob 字符大对象,超过255个字符的都要采用clob字符大对象,最多可以存储4G的字符串,比如:存储一篇文章等;
blob 二进制大对象,专门用来存储图片、声音、视频等流媒体数据,往blob类型的字段上出啊如数据时,例如插入一个图片、视频等,必须使用IO流。
案例:t_movie 电影表 (专门存储电影信息)
编号 no(bigint) |
名字 name(varchar) |
故事情节 history(clob) |
上映日期 showtime(date) |
时长 time(double) |
海报 image(blob) |
类型 type(char) |
10000 | 哪吒 | 略 | 2019-10-11 | 2.5 | 略 | ‘动画片' |
10001 | 柯南 | 略 | 2019-10-11 | 3.0 | 略 | ’动画片‘ |
3. 删除表?
drop table 表名; // 若这张表不存在时,会报错
drop table if exists 表名; // 如果这张表存在的话,删除该表
4. 创建一个学生表?
// 学生信息:学号、姓名、性别、年龄、邮箱地址
create table t_student(no int,name varchar,sex char(1),age int,e-mail varchar);
// 上条语句报错
// 错误信息:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'sex char(1),age int,e-mail varchar)' at line 1
// 错误原因: varchar(255) 括号内的建议长度不能省
create table t_student(no int,name varchar(32),sex char(1),age int(3),email varchar(255));
71 - 74
八、插入数据 insert
1. 语法格式:
inset into 表名(字段名1,字段名2,字段名3...) value(值1,值2,值3);
// 注意: 字段名和值一一对应
insert into t_student(no,name,sex,age,email) value(1,'zhangsan','m',20,'[email protected]');
insert into t_student(name,no,age,sex,email) value('lisi',1,21,'m','[email protected]');
+------+----------+------+------+---------------+
| no | name | sex | age | email |
+------+----------+------+------+---------------+
| 1 | zhangsan | m | 20 | [email protected] |
| 1 | lisi | m | 21 | [email protected] |
+------+----------+------+------+---------------+
// 注意: insert 语句但凡执行成功了,那么必然会多一条记录;没有给其他字段指定值的话,默认值是NULL
2. 建表时,可指定默认值(不修改默认为NULL)
// 删除刚刚建的表
drop table t_student;
// 建表,指定sex默认为'm'
create table t_student(
no int,
name varchar(32),
sex char(1) default 'm',
age int(3),
email varchar(255)
);
// 查看表结构
desc t_student;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| no | int(11) | YES | | NULL | |
| name | varchar(32) | YES | | NULL | |
| sex | char(1) | YES | | m | | // 默认值改成'm'
| age | int(3) | YES | | NULL | |
| email | varchar(255) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
3. insert 语句中的字段名可以省略
注意:前面的字段名省略的话,等于都写上,所以值也要都写上,且不能颠倒顺序
insert into t_student value(2,'lisi','f',20,'[email protected]');
select * from t_student;
+------+------+------+------+--------------+
| no | name | sex | age | email |
+------+------+------+------+--------------+
| 2 | lisi | f | 20 | [email protected] |
+------+------+------+------+--------------+
4. insert 插入日期
(了解) 格式化数字: format(数字,'格式')
select ename,format(sal,'$999,999') as sal from emp; // 千分位
+--------+-------+
| ename | sal |
+--------+-------+
| SMITH | 800 |
| ALLEN | 1,600 |
| WARD | 1,250 |
| JONES | 2,975 |
| MARTIN | 1,250 |
| BLAKE | 2,850 |
| CLARK | 2,450 |
| SCOTT | 3,000 |
| KING | 5,000 |
| TURNER | 1,500 |
| ADAMS | 1,100 |
| JAMES | 950 |
| FORD | 3,000 |
| MILLER | 1,300 |
+--------+-------+
4.1 str_to_date:将字符串varchar类型转换成date类型;
语法格式:str_to_date('字符串日期','日期格式');
MySQL日期格式: %Y 年 %m 月 %d 日 %h 时 %i 分 %s 秒
// 建表
drop table if exists t_user;
create table t_user(
id int(11),
name varchar(32),
birth date
);
// *插入数据
insert into t_user(id,name,birth)
value(1,'zhangsan',str_to_date('01-10-1990','%d-%m-%Y')); // 1990-10-01
// str_to_date: 将字符串varchar转换为日期类型date,通常使用在插入insert方面,因为插入的时候需要一个日期类型的数据,需要通过该函数将字符串转换成date
// 若提供的日期字符串是该格式: %Y-%m-%d,str_to_date可省
insert into t_user(id,name,birth)
value(2,'lisi','1997-01-06');
// 查询
select * from t_user;
+------+----------+------------+
| id | name | birth |
+------+----------+------------+
| 1 | zhangsan | 1990-10-01 |
| 2 | lisi | 1997-01-06 |
+------+----------+------------+
4.2 date_format:将date类型转换成具有一定格式的varchar字符串类型;
语法格式: date_format(日期类型数据,'日期格式')
使用场景: 该函数通常在查询日期时,设置展示的日期格式。
查询的时候可以以某个特定的日期格式展示吗?
select id,name,date_format(birth,'%m/%d/%Y') as birth from t_user;
+------+----------+------------+
| id | name | birth |
+------+----------+------------+
| 1 | zhangsan | 10/01/1990 |
| 2 | lisi | 01/06/1997 |
+------+----------+------------+
select id,name,birth from t_user;
+------+----------+------------+
| id | name | birth |
+------+----------+------------+
| 1 | zhangsan | 1990-10-01 |
| 2 | lisi | 1997-01-06 |
+------+----------+------------+
// 注意: 以上的SQL语句实际进行了默认的日期格式化,自动将数据库中的date类型转换成varchar类型,并且采用的格式时MySQL默认的日期格式: '%Y-%m-%d'
5. date 与 datetime 两个类型的区别?
date 是短日期,只包含年月日信息;datetime 是长日期,包含年月日时分秒信息;
drop table if exists t_user;
create table t_user(
id int,
name varchar(32),
birth date,
create_time datetime
);
// 插入数据
// MySQL 短日期默认格式: %Y-%m-%d
// MySQL 长日期默认格式: %Y-%m-%d %h:%i:%s
insert into t_user(id,name,birth,create_time) value(1,'zhangsan','1997-01-06','2022-03-11 16:36:30');
+------+----------+------------+---------------------+
| id | name | birth | create_time |
+------+----------+------------+---------------------+
| 1 | zhangsan | 1997-01-06 | 2022-03-11 16:36:30 |
+------+----------+------------+---------------------+
// 在MySQL当中如何获取系统当前时间呢?-- now(),并且获取的时间是datetime类型
insert into t_user(id,name,birth,create_time) value(1,'zhangsan','1997-01-06',now());
+------+----------+------------+---------------------+
| id | name | birth | create_time |
+------+----------+------------+---------------------+
| 1 | zhangsan | 1997-01-06 | 2022-03-11 16:36:30 |
| 1 | zhangsan | 1997-01-06 | 2022-03-11 16:39:44 |
+------+----------+------------+---------------------+
75 - 76
九、更新数据 update (DML)
1. 语法格式
update 表名 set 字段名1=值1,字段名2=值2,字段名3=值3... where 条件;
// 注意: 没有条件限制会导致所有数据全部更新
// 举例
select * from t_user;
+------+----------+------------+---------------------+
| id | name | birth | create_time |
+------+----------+------------+---------------------+
| 1 | zhangsan | 1997-01-06 | 2022-03-11 16:36:30 |
| 1 | zhangsan | 1997-01-06 | 2022-03-11 16:39:44 |
+------+----------+------------+---------------------+
update t_user set name = 'jack',birth = '2000-10-11' where id = 1;
select * from t_user;
+------+------+------------+---------------------+
| id | name | birth | create_time |
+------+------+------------+---------------------+
| 1 | jack | 2000-10-11 | 2022-03-11 16:36:30 |
| 1 | jack | 2000-10-11 | 2022-03-11 16:39:44 |
+------+------+------------+---------------------+
十、删除数据 delete (DML)
1. 语法格式
delete from 表名 where 条件;
// 注意:没有where条件,会删除整张表的数据
delete from t_user where id = 1; // 删除id为1的这行数据
delete from t_user; // 删除所有数据
77 - 78 以上内容的复习( 暂缓,完全学完之后,复习以上内容完成练习题 )
79 - 83
十一、insert 语句可以一次插入多条记录吗? -- 可以
1. 语法格式
insert into t_user(字段名1,字段名2) values(),(),(),();
// 清空t_user中所有数据
delete from t_user;
desc t_user; // 查看表结构
+-------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(32) | YES | | NULL | |
| birth | date | YES | | NULL | |
| create_time | datetime | YES | | NULL | |
+-------------+-------------+------+-----+---------+-------+
// 插入多条数据
insert into t_user(id,name,birth,create_time) values
(1,'zs','1980-10-11',now()),
(2,'list','1981-10-11',now()),
(3,'wangwu','1981-10-11',now());
// select * from t_user;
+------+--------+------------+---------------------+
| id | name | birth | create_time |
+------+--------+------------+---------------------+
| 1 | zs | 1980-10-11 | 2022-03-15 16:18:25 |
| 2 | list | 1981-10-11 | 2022-03-15 16:18:25 |
| 3 | wangwu | 1981-10-11 | 2022-03-15 16:18:25 |
+------+--------+------------+---------------------+
十二、快速复制表 (了解)
原理:将一个查询结果当作一张表新建!!这个可以完成表的快速复制。
create table emp2 as select * from emp;
// 创建表emp2,复制查询结果的内容
create table mytable as select empno,ename from emp where job = 'MANAGER';
十三、如何将查询结果插入到一张表中?insert 相关内容 (了解)
insert into dept_bak select * from dept;
// 将 select * from dept 的查询结果插入 dept_bak 中
// 注意:查询结果的结构要符合要插入的表结构
十四、快速删除表中的数据?-- truncate (DDL)
delete 语句删除数据的原理:(速度慢) (DML)
表中的数据被删除,但该数据在硬盘上的真实存储空间不会被释放。
缺点:删除效率较低;
优点:支持回滚,可恢复已删除数据。
truncate 语句删除数据的原理: (速度快)
表被一次截断,物理删除。
缺点:不支持回滚;
优点:删除效率高、速度快。
// truncate 语法格式
truncate table 表名;
适用场景:表非常大,上亿条记录,删除的时候若使用delete,可能要执行1个小时才能删完,效率较低。可以选择使用truncate删除表中的数据,只需要不到1秒,但使用truncate之前需要确定是否确定删除,并警告删除之后不可恢复。
注意:区分删除表操作-- drop table 表名;
十五、对表结构的增删改?--- 添加一个字段、删除一个字段、修改一个字段 (了解)
DDL 语句:create、drop、alter
84 - 96
十六、约束(非常重要)
1. 什么是约束?
在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性、有效性。
2. 约束包括哪些?
非空约束 not null、唯一性约束 unique、主键约束 primary key(简称PK)、外键约束 foreign key(简称FK)、检查约束 check(mysql不支持,oracle支持)
3. 非空约束 not null
not null 约束的字段不能为NULL,必须插入数据
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(2); // 执行该命令会报错,提示name字段没有默认值
注: xxx.sql 这种文件被称为sql脚本文件,sql脚本文件中编写了大量的sql语句。我们执行sql脚本文件的时候,该文件中所有的sql语句会全部执行!批量的执行sql语句,可以使用sql脚本文件。
在mysql中如何执行sql脚本?
source D:\course\03-MySQL\document\vip.sql // 脚本文件路径
4. 唯一性约束 unique
唯一性约束unique约束的字段不能重复,但可以为NULL
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) unique, // 唯一性约束
);
insert into t_vip(id,name) values(1,'zhangsan');
insert into t_vip(id,name) values(2,'zhangsan'); // 这里会报错,因为name字段不能重复
insert into t_vip(id) values(2); // name可以为NULL
insert into t_vip(id) values(3); // name可以为NULL NULL不算重
需求:name 和 email 两个字段联合起来具有唯一性?-- 表级约束
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255), // 约束直接添加到列后面的,叫做列级约束
email varchar(255),
unique(name,email) // 表示name和email联合起来具有唯一性
// 约束没有添加在列后面,这种约束被称为表级约束
);
// 测试
insert into t_vip(id,name,email) values(1,'zhangsan','[email protected]');
insert into t_vip(id,name,email) values(2,'zhangsan','[email protected]');
什么是列级约束?-- 约束直接添加到列后面的,叫做列级约束;
什么是表级约束? -- 约束没有添加到列后面,这种约束被称为表级约束;
什么时候使用表级约束? -- 需要给多个字段联合起来添加某一个约束时,需要使用表级约束。
not null 没有表级约束,只有列级约束
unique 可以和 not null联合使用? -- 可以
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) not null unique, // not null 与 unique 联合使用,不为空且唯一
email varchar(255)
);
desc t_vip;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(255) | NO | PRI | NULL | |
| email | varchar(255) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
在mysql当中,如果一个字段同时被not null 和 unique约束的话,该字段自动变成主键字段。
(注意:oracle中不一样!)
5. 主键约束(primary key,简称PK) (非常重要)
5.1 主键约束的相关术语
主键约束:就是一种约束;
主键字段:该字段上添加了主键约束,这样的字段叫做主键字段;
主键值:主键字段中的每一个值都叫做主键值;
5.2 什么是主键?有啥用?
主键值是每一行记录的唯一标识;(类似于重名的人,身份证号是主键,肯定是不同的)
任何一张表都应该有主键,没有主键,表无效;
5.3 主键的特征:not null + unique (主键值不能是NULL,同时也不能重复!)
5.4 如何给一张表添加主键约束呢?
drop table if exists t_vip;
create table t_vip(
id int primary key, // 主键约束,不能为NULL且唯一
name varchar(255)
);
insert into t_vip(id,name) values(1,'zhangsan');
insert into t_vip(id,name) values(1,'lisi'); // 报错:因为id是主键,不能重复
insert into t_vip(name) values('wangwu'); // 报错: 因为id不能为NULL
5.5 可以使用表级约束来添加主键吗?-- 可以
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255),
primary key(id) // 表级约束:id为主键
);
5.6 表级约束主要是给多个字段联合起来添加约束?
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255),
primary key(id,name) // 表级约束:id和name联合起来为主键,叫做复合主键
);
注: 一个字段做主键叫单一主键,两个字段联合起来做主键叫做复合主键,但在实际开发中不建议使用复合主键,建议使用单一主键。
5.7 一个表中主键约束能加两个吗?--不能
结论:一张表,主键约束只能添加一个。
5.8 主键值建议使用 int、bigint、char等类型,不建议使用varchar做主键;
主键值一般都是数字,一般都是定长的。
5.9 主键除了分为单一主键和复合主键,还可以按自然主键和业务主键分类
自然主键:主键值是一个自然数,和业务没关系;
业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值;
在实际开发中使用自然主键多一些,因为主键只要做到不重复就行,不需要有意义;主键一旦和业务挂钩,那么当业务发生变动时,可能会影响到主键值,所以业务主键不建议使用,尽量使用自然主键。
5.10 在MySQL中,有一种机制,可以帮助我们自动维护一个主键值?
-- auto_increment 从1开始,步长为1递增
drop table if exists t_vip;
create table t_vip(
id int primary key auto_increment, // 自动维护主键id
name varchar(255)
);
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
select * from t_vip;
+----+----------+
| id | name |
+----+----------+
| 1 | zhangsan |
| 2 | zhangsan |
| 3 | zhangsan |
| 4 | zhangsan |
+----+----------+
6. 外键约束(foreign key,简称FK) (非常重要)
6.1 外键约束涉及的相关术语:
外键约束:一种约束;
外键字段:该字段上添加了外键约束;
外键值:外键字段当中的每一个值;
6.2 业务背景:请设计数据库表,描述“班级和学生”的信息?
方案一:班级和学生存储在一张表中
no(pk) | name | classno | classname |
1 | jack | 100 | 北京市大兴区亦庄镇第二中学高三1班 |
2 | lucky | 100 | 北京市大兴区亦庄镇第二中学高三1班 |
3 | lisi | 101 | 北京市大兴区亦庄镇第二中学高三2班 |
以上方案的缺点:数据冗余、空间浪费
方案二:班级一张表,学生一张表
t_class 班级表
classno(pk) | classname |
100 | 北京市大兴区亦庄镇第二中学高三1班 |
101 | 北京市大兴区亦庄镇第二中学高三2班 |
t_student 学生表
no(pk) | name | cno(FK) |
1 | jack | 100 |
2 | lucky | 100 |
3 | lisi | 101 |
注意: 当cno字段没有任何约束的时候,可能会导致数据无效,如出现一个102,但是102班级不存在,所以为了保证cno字段中的值都是100和101,需要给cno字段添加外键约束。那么:cno字段就是外键字段,cno中的值就是外键值。
注意:t_class 是父表,t_student 是子表
删除表的顺序?-- 先删除子表,再删除父表;
创建表的顺序? -- 先创建父表,再创建子表;
删除数据的顺序? -- 先删除子表的数据,再删除父表的数据;
插入数据的顺序?-- 先插入父表的数据,再插入子表的数据;
drop table if exists t_student;
drop table if exists t_class;
create table t_class(
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) reference t_class(classno) // 外键约束cno,指向t_class表中的classno字段
);
insert into t_class(classno,classname) values(100,'北京市大兴区亦庄镇第二中学高三1班');
insert into t_class(classno,classname) values(101,'北京市大兴区亦庄镇第二中学高三2班');
insert into t_student(name,cno) values(jack,100);
insert into t_student(name,cno) values(lucky,100);
insert into t_student(name,cno) values(lisi,101);
select * from t_class;
select * from t_student;
思考: 子表中受外键约束的字段,所指向的父表中的字段是否必须为主键?
不一定是主键,但至少具有 unique 约束。
外键值可以为NULL
97 - 101
十七:存储引擎(了解)
1. 什么是存储引擎,有什么用?
存储引擎是MySQL中特有的一个术语,其他数据库中没有。实际上存储引擎是一个表存储/组织数据的方式,不同的存储引擎,表存储数据的方式不同。
2. 如何给表添加/指定存储引擎?
show create table t_student;
+-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t_student | CREATE TABLE `t_student` (
`no` int(11) DEFAULT NULL,
`name` varchar(32) DEFAULT NULL,
`sex` char(1) DEFAULT 'm',
`age` int(3) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 | // 指定存储引擎和默认编码方式
+-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
在建表的时候可以在最后小括号")"的右边使用:
ENGINE来指定存储引擎, CHARSET来指定这张表的字符编码方式。
MySQL默认的存储引擎: InnoDB;
MySQL默认的字符编码方式: utf8
3. 如何查看当前版本MySQL支持哪些存储引擎?
show engines \G
*************************** 1. row ***************************
Engine: FEDERATED
Support: NO
Comment: Federated MySQL storage engine
Transactions: NULL
XA: NULL
Savepoints: NULL
*************************** 2. row ***************************
Engine: MRG_MYISAM
Support: YES
Comment: Collection of identical MyISAM tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 3. row ***************************
Engine: MyISAM
Support: YES
Comment: MyISAM storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 4. row ***************************
Engine: BLACKHOLE
Support: YES
Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
XA: NO
Savepoints: NO
*************************** 5. row ***************************
Engine: CSV
Support: YES
Comment: CSV storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 6. row ***************************
Engine: MEMORY
Support: YES
Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 7. row ***************************
Engine: ARCHIVE
Support: YES
Comment: Archive storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 8. row ***************************
Engine: InnoDB
Support: DEFAULT
Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
XA: YES
Savepoints: YES
*************************** 9. row ***************************
Engine: PERFORMANCE_SCHEMA
Support: YES
Comment: Performance Schema
Transactions: NO
XA: NO
Savepoints: NO
MySQL支持九大存储引擎,当前5.5.36支持8个。版本不同支持情况不同。
4. 关于MySQL常用的存储引擎?
MyISAM、InnoDB、MEMORY
102 - 112
十八、事务(重点!!!必须理解、掌握)
1. 什么是事务?
一个事务其实就是一个完整的业务逻辑;是一个最小的工作单元,不可再分。
什么是一个完整的业务逻辑? 假设转账,从A账户向B账户转账10000;将A账户的钱减去10000(update语句),将B账户的钱加上10000(update语句),这就是一个完整的业务逻辑。
以上操作是一个最小的工作单元,要么同时成功,要么同时失败,不可再分。
这两个update语句要求必须同时成功或者同时失败,这样才能保证钱是正确的。
本质上,一个事务就是多条DML语句同时成功,或者同时失败。
2. 只有DML语句(insert、delete、update)才与事务有关系
因为只有以上的三条语句是数据库表中数据增、删、改,只要操作一旦涉及到数据的增、删、改,那么就一定要考虑安全问题。
3. 假设所有的业务,只要一条DML语句就能完成,还有必要存在事务机制吗?
答:没有必要。正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事都能用一条DML语句搞定,那么事务则没有存在的价值。
4. 事务是怎么做到多条DML语句同时成功和同时失败的?
InnoDB存储引擎:提供一组用来记录事务性活动的日志文件;
事务开启了:insert insert delete update update 事务结束了!
在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中;
在事务的执行过程中,我们可以提交事务,也可以回滚事务;
提交事务? -- 清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中;提交事务标志着,事务的结束,并且是一种全部成功的结束。
回滚事务? -- 将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件;回滚事务标志着,事务的结束,并且是一种全部失败的结束。
5. 怎么提交事务?怎么回滚事务? 事务:transaction
提交事务:commit
回滚事务:rollback(回滚只能回滚到上一次的提交点!)
在MySQL当中默认的事务行为是怎样的?
MySQL默认情况下是支持自动提交事务的。自动提交:每执行一条DML语句,则提交一次。
这种自动提交实际上不符合开发习惯。因为一个业务通常是需要多条DML语句共同执行才能完成,为了保证数据的安全,必须要求同时成功之后再提交。
如何关闭MySQL的自动提交机制?
start transaction; // 关闭自动提交机制
事务演示
------------------回滚事务-------------------------
mysql> start transaction; // 暂停自动提交
Query OK, 0 rows affected (0.00 sec)
mysql> insert into dept_bak values(10,'abc','tj');
Query OK, 1 row affected (0.00 sec)
mysql> insert into dept_bak values(10,'abc','tj');
Query OK, 1 row affected (0.00 sec)
mysql> select * from dept_bak;
+--------+-------+------+
| deptno | dnmae | loc |
+--------+-------+------+
| 10 | abc | tj |
| 10 | abc | tj |
+--------+-------+------+
2 rows in set (0.00 sec)
mysql> rollback; // 回滚到上一个提交点
Query OK, 0 rows affected (0.01 sec)
mysql> select * from dept_bak; // 上一个提交点没有数据
Empty set (0.00 sec)
------------------提交事务-----------------
start transaction;
.......
commit; // 提交事务
6. 事务的4个特性?
原子性:说明事务是最小的工作单元,不可再分;
一致性:所有事务要求在同一个事务当中,所有操作必须同时成功,或者同时失败,以保证数据的一致性;
隔离性:A事务和B事务之间具有一定的隔离;相当于A教室与B教室中间有一堵墙;
持久性:事务最终结束的一个保障。事务提交,就相当于将没有保存到硬盘上的数据保存到硬盘上。
7. 重点研究一下 事务的隔离性
A教室和B教室中间有一道墙,这道墙可以很厚,也可以很薄。这就是事务的隔离级别,这道墙越厚,表示隔离级别就越高。
事务与事务之间的隔离级别有哪些呢?--4个级别
读未提交:read uncommitted (最低的隔离级别) (没有提交就读到了)
什么是读未提交?-- 事务A可以读取到事务B未提交的数据;
这种隔离级别存在的问题就是:脏读现象!(Dirty Read),我们称读到了脏数据;
这种隔离级别一般都是理论上的,大多数的数据库隔离级别都是二档起步!
读已提交:read committed (提交之后才能读到)
什么是读已提交? -- 事务A只能读取到事务B提交之后的数据;
这种隔离级别解决了什么问题?-- 解决了脏读现象;
这种隔离级别存在什么问题?-- 不可重复读取数据;
什么是不可重复读取数据呢?-- 在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读取。
这种隔离级别是比较真实的数据,每一次读到的数据是绝对的真实;
oracle数据库默认的隔离级别:读已提交
可重复读:repeatable read (提交之后也读不到,永远读取的都是刚开启事务时的数据)
什么是可重复读取? -- 事务A开启之后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。
可重复读解决了什么问题?-- 解决了不可重复读问题;
可重复读存在什么问题?-- 可能会出现幻影读;每一次读取到的数据都是幻象,不够真实;
MySQL中默认的事务隔离级别就是可重复读!
序列化/串行化:serializable (最高的隔离级别)
这是最高隔离级别,效率最低,解决了所有的问题;这种隔离级别表示事务排队,不能并发!
synchronized, 线程同步(事务同步)
每一次读取到的数据都是最真实的,并且效率是最低的。
8. 验证各种隔离级别
验证:read uncommitted
// 查看当前的隔离级别
select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ | // MySQL默认的隔离级别
+-----------------+
被测试的表t_user
// 验证:read uncommitted
set global transaction isolation level read uncommitted;
事务A 事务B
-----------------------------------------------------------------
use bjpowernode; use bjpowernode;
start transaction; start transaction;
insert into t_user value('zhangsan');
select * from t_user;
// 未提交事务,就能查到插入的信息
验证:read committed
被测试的表t_user
// 验证:read committed
set global transaction isolation level read committed;
事务A 事务B
-----------------------------------------------------------------
use bjpowernode; use bjpowernode;
start transaction; start transaction;
insert into t_user value('zhangsan');
select * from t_user;
// 未提交事务,查不到插入的信息
commit; // 提交事务
select * from t_user;
// 已提交事务,可以查到插入的信息
验证:repeatable read
被测试的表t_user
// 验证:repeatable read
set global transaction isolation level repeatable read;
事务A 事务B
-----------------------------------------------------------------
use bjpowernode; use bjpowernode;
start transaction; start transaction;
select * from t_user;
// 读到的信息记为A
insert into t_user values('zhangsan');
insert into t_user values('lisi');
commit;
select * from t_user;
// 不管事务B怎么操作,读到的还是只有信息A这一条记录(幻象)
验证:serializable
被测试的表t_user
// 验证:serializable
set global transaction isolation level serializable;
事务A 事务B
-----------------------------------------------------------------
use bjpowernode; use bjpowernode;
start transaction; start transaction;
select * from t_user;
insert into t_user values('abc');
commit;
select * from t_user; // 可以查到事务A中插入的信息
114 - 122
十九、索引
1. 什么是索引?
索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制;
一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引;
索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制;
select * from t_user where name = 'jack';
// 以上SQL语句会取name字段上扫描(因为查询条件是:name = 'jack')
MySQL在查询方面主要就是两种方式:全表扫描、根据索引检索;
索引为什么排序?-- 因为只有排序了才会有区间查找这一说!(缩小扫描范围其实就是扫描某个区间罢了!)
在MySQL数据库当中索引也是需要排序的,并且这个索引的排序和TreeSet数据结构相同。TreeSet(TreeMap) 底层是一个自平衡的二叉树!在MySQL当中索引是一个B-Tree数据结构。
2. 索引的实现原理
假设有一张用户表:t_user
id(PK) name 每一行记录在硬盘上都有物理存储编号
-----------------------------------------------------------------
100 zhangsan 0x1111
120 lisi 0x2222
99 wangwu 0x8888
88 zhaoliu 0x9999
101 jack 0x6666
55 lucky 0x5555
130 tom 0x7777
提醒1:在任何数据库当中,主键上都会自动添加索引对象,即id字段上自动有索引。另外在MySQL当中,一个字段上如果有unique约束的话,也会自动创建索引对象。
提醒2:在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。
提醒3:在MySQL当中,索引是 一个单独的对象,不同的存储引擎以不同的形式存在,在MyISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中,索引存储在一个逻辑名称叫做tablespace的当中。在MEMORY存储引擎当中索引被存储在内存当中。不管索引存储在哪里,索引在MySQL当中都是一个树的形式存在。(自平衡二叉树:B-Tree)
索引实现原理的大致流程:
3. 在MySQL中,主键上、unique字段上都会自动添加索引!!!
什么条件下,我们会考虑给字段添加索引?
条件1:数据量庞大;
条件2:该字段经常出现在where的后面,以条件的形式存在,也就是说这个字段总是被扫描;
条件3:该字段很少进行DML操作(因为DML之后,索引需要重新排序);
建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统性能;
建议通过主键查询,通过unique约束的字段进行查询,这样效率较高。
4. 索引如何创建?如何删除?
// 创建索引
// 给emp表的ename字段添加索引,起别名为emp_ename_index
create index emp_ename_index on emp(ename);
// 删除索引
// 将emp表上的emp_ename_index索引对象删除
drop index emp_ename_index on emp;
5. 在MySQL中如何查看一个SQL语句是否使用了索引进行检索?
explain select * from emp where ename = 'KING';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
// 扫描14条记录,说明没有使用索引。type - ALL
// ename字段创建索引
create index emp_ename_index on emp(ename);
// 再查一次
explain select * from emp where ename = 'KING';
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_ename_index | emp_ename_index | 33 | const | 1 | Using where |
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
// type - ref 扫描1条记录,说明使用了索引
6. 索引何时会失效?
情况一:模糊查询时条件以‘%’开头,索引会失效;
select * from emp where ename like '%T';
// ename上即使添加了索引,也不会按索引查询,而是全表查询;
// 原因:因为模糊匹配当中以"%"开头了;
// 尽量避免模糊查询的时候以"%"开始;
情况二:使用or的时候可能会失效。如果使用or,则要求or两边的条件字段都要有索引,才会走索引,否则,即使一个字段没有索引、一个字段有索引,有索引的字段索引也会失效。这就是不建议使用or的原因。
情况三:使用复合索引的时候,没有使用左侧的列查找,索引会失效;
什么是复合索引? -- 两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引;
create index emp_job_sal_index on emp(job,sal);
// 使用左侧的列job查找
explain select * from emp where job = 'MANAGER';
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_job_sal_index | emp_job_sal_index | 30 | const | 3 | Using where |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
// type - ref 说明使用了索引查找
// 使用右侧的列sal查找
explain select * from emp where sal = 800;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
// type - ALL 索引失效
情况四:在where当中索引列参加了运算,索引会失效;
create index emp_sal_index on emp(sal);
explain select * from emp where sal = 800; // 索引查找
explain select * from emp where sal + 1 = 800; // 索引列参与运算,索引失效
情况五:在where当中,索引列使用了函数,索引会失效;
create index emp_ename_index on emp(ename);
explain select * from emp where lower(ename) = 'SMITH'; // 索引会失效
....
7. 索引是各种数据库进行优化的重要手段,优化时优先考虑的因素就是索引
索引在数据库中分很多类:
单一索引:一个字段上添加索引;
复合索引:两个及以上字段上添加索引;
主键索引:主键上添加索引;
唯一性索引:具有unique约束的字段上添加索引;
注:唯一性较弱的字段上添加索引用处不大。
123 -
二十、视图
1. 什么是视图?
view: 站在不同的角度去看待同一份数据;
2. 怎么创建视图对象?怎么删除视图对象?
// 表复制
create table dept2 as select * from dept;
// dept2表中的数据
select * from dept2;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
// 创建视图对象
create view emp_view as select * from emp;
// 删除视图对象
drop view dept2_view;
// 注意:只有DQL语句才能以view的形式创建,create view view_name_as 这里的语句必须是DQL语句
3. 视图可以做什么?
可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作!(视图的特点:通过对视图的操作,会影响到原表数据)
4. 视图对象在实际开发中到底有什么用?
假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用,每一次使用这个SQL语句的时候都需要重新编写,很长,很麻烦,如何处理呢?
-- 可以把这条复杂的SQL语句以视图对象的形式新建,在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发,并且利于后期的维护。因为修改的时候只需要修改一个位置即可,只需要修改视图对象所映射的SQL语句。
注:视图和表一样,可以对视图进行增删改查等操作,且视图对象也存储在硬盘上,不会消失。
视图对应的语句只能是DQL语句,但在视图对象创建完成后,可以对视图进行增删改查等操作。
5. DBA常用命令(了解)
重点掌握:数据的导入和导出(数据的备份)
数据导出?
注意:在windows的dos命令窗口中
C:\Users\张亚辉>mysqldump bjpowernode>D:\bjpowernode.sql -u root -p密码
// 可以导出指定的表emp
C:\Users\张亚辉>mysqldump bjpowernode emp>D:\bjpowernode.sql -u root -p密码
数据导入?
注意:需要先登录到MySQL数据库服务器上;
然后创建数据库:create database bjpowernode;
使用数据库:use bjpowernode;
然后初始化数据库:source D:\bjpowernode.sql
二十一、数据库设计三范式
1. 什么是数据库设计范式?
数据库表的设计依据,教你怎么进行数据库表的设计;
2. 数据设计范式有哪些?
第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分;
第二范式:(建立在第一范式基础上)要求所有非主键字段完全依赖主键,不要产生部分依赖;
第三范式:(建立在第二范式基础上)要求所有非主键字段直接依赖主键,不要产生传递依赖;
注:三范式是面试官常问的
设计数据库表时,按照以上的范式进行,可以避免表中数据的冗余,空间的浪费;
3. 第一范式(最核心最重要的范式,所有表的设计都需要满足)
要求任何一张表必须有主键,每一个字段原子性不可再分;
学生编号 学生姓名 联系方式
----------------------------------
1001 张三 [email protected],133202932
1002 李四 [email protected],123323232
1001 王五 [email protected],1232322323
以上是学生表,满足第一范式吗?
--- 不满足。第一:没有主键;第二:联系方式可以再分为邮箱地址和电话;
如何修改学生表,使其满足第一范式?
学生编号(pk) 学生姓名 邮箱地址 联系电话
-------------------------------------------------------
1001 张三 [email protected] 133202932
1002 李四 [email protected] 123323232
1003 王五 [email protected] 123232232
4. 第二范式
(建立在第一范式基础上)要求所有非主键字段完全依赖主键,不要产生部分依赖;
// 下表描述了学生和老师的关系(多对多关系)
学生编号 学生姓名 教师编号 教室姓名
-----------------------------------------------
1001 张三 001 王老师
1002 李四 002 赵老师
1003 王五 001 王老师
1001 张三 002 赵老师
分析以上的表是否满足第一范式? -- 不满足
修改上表,使其满足第一范式:学生编号与教师编号两个字段联合做主键,即复合主键(pk:学生编号+教师编号)
学生编号 教师编号(pk) 学生姓名 教室姓名
-----------------------------------------------
1001 001 张三 王老师
1002 002 李四 赵老师
1003 001 王五 王老师
1001 002 张三 赵老师
// 修改后的表满足第一范式,但满足第二范式吗?
-- 不满足,'张三'依赖'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
背口诀:多对多怎么设计? -- 多对多,三张表,关系表+两个外键!!!!
5. 第三范式
(第三范式建立在第二范式的基础上) 要求所有非主键字段必须直接依赖主键,不要产生传递依赖。
学生编号(pk) 学生姓名 班级编号 班级名称
---------------------------------------------------
1001 张三 01 一年一班
1002 李四 02 一年二班
1003 王五 03 一年三班
1004 赵六 03 一年三班
以上表的设计是描述班级和学生的关系,一对多;
分析以上表是否满足第一范式? -- 有主键,满足第一范式;
分析以上表是否满足第二范式? -- 因为主键不是复合主键,没有产生部分依赖,满足第二范式;
分析以上表是否满足第三范式? -- 一年一班依赖01,01依赖1001,产生了传递依赖,不符合第三范式的要求,产生了数据的冗余;
那么应该怎么设计一对多呢? --- 口诀:一对多,两张表,多的表加外键!!!
班级表:
班级编号(pk) 班级名称
---------------------------------
01 一年一班
02 一年二班
03 一年三班
学生表:
学生编号(pk) 学生姓名 班级编号(fk)
------------------------------------------
1001 张三 01
1002 李四 02
1003 王五 03
1004 赵六 03
6. 总结表的设计?
一对多:两张表,多的表加外键!!!
多对多:三张表,关系表两个外键!!!
一对一:在实际开发中,可能存在一张表字段太多,太庞大,需要拆分表
没有拆分表之前:一张表
t_user表
id login_name login_pwd real_name email adress.......
-------------------------------------------------------------------
1 zhangsan 123 张三 zhangsan@xxx
2 lisi 123 李四 lisi@xxx
....
这种庞大的表建议拆分为两张:1.t_login 登录信息表 2.t_user 用户信息表
t_login
id(pk) login_name login_pwd
------------------------------------
1 zhangsan 123
2 lisi 123
t_user
id(pk) real_name email adress .... login_id(fk+unique)
-----------------------------------------------------------------------
100 张三 zhangsan@xxx 1
200 李四 lisi@xxx 2
口诀:一对一,外键唯一!!!!
7. 理论与实践的偏差
数据库设计三范式是理论上的,实践与理论有时候会有偏差,最终目的都是为了满足客户需求,有时候会拿冗余换执行速度。因为在SQL中,表和表之间的连接次数越多,效率越低(笛卡尔积),有时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,并且对于开发人员来说,SQL语句的编写难度也会降低。