Bootstrap

MySQL入门学习(超详细+34练习题)

MySQL入门学习(超详细+34练习题)

学习B站老杜MySQL后总结笔记。收藏、点赞、转发。

1.简介

什么是数据库?什么是数据库管理系统?什么是SQL?他们之间的关系是什么?

数据库:

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

数据库管理系统:

  • DataBaseManagement,简称DBMS。
  • 数据库管理系统是专门用来管理数据库中数据的,数据库管理系统可以对数据库当中的数据进行增删改查。
  • 常见的数据库管理系统:MySQL、Oracle、MS SqlServer、DB2、sybase等…

SQL:结构化查询语言

  • 程序员需要学习SQL语句,程序员通过编写SQL语句,然后DBMS负责执行SQL语句,最终来完成数据库中数据的增删改查操作。

  • SQL是一套标准,程序员主要学习的就是SQL语句,这个SQL在mysql中可以使用,同时在Oracle中也可以使用,在DB2中也可以使用。

三者之间的关系?

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

先安装数据库管理系统MySQL,然后学习SQL语句怎么写,编写SQL语句之后,DBMS
对SQL语句进行执行,最终来完成数据库的数据管理。

2.常用命令

#注意:以上的命令不区分大小写,都行。

#注意:mysql是不见“;”不执行,“;”表示结束!
#\c用来终止一条命令的输入。

#本地登录
mysql -uroot -p
密码

#退出mysql
exit

#查看mysql中有哪些数据库?
show databases;

#使用某个数据库
use test;

#创建数据库
create database 数据库名;

#数据库下有哪些表
show tables;

#查看mysql数据库的版本号
select version();

#查看当前使用的是哪个数据库
select database();

#查看表结构
desc 表名;
describe dept;

3.表Table

数据库当中最基本的单元是表:table

什么是表table?为什么用表来存储数据呢?

数据库当中是以表格的形式表示数据的。因为表比较直观。

任何一张表都有行和列:

  • 行(row):被称为数据/记录。
  • 列(column):被称为字段。

4.SQL语句的分类

SQL语句有很多,最好进行分门别类,这样更容易记忆。

DQL(Data Query Language):数据查询语言(凡是带有select关键字的都是查询语句)

  • select:查

DML(Data Manipulation Language):数据操作语言(凡是对表当中的数据进行增删改的都是DML)

  • insert:增
  • delete:删
  • update:改

DDL(Data Definition Language):数据定义语言,凡是带有create、drop、alter的都是DDL。DDL主要操作的是表的结构。不是表中的数据。这个增删改和DML不同,这个主要是对表结构进行操作。

  • create:新建,等同于增
  • drop:删除
  • alter:修改

TCL(Transaction Control Language):事务控制语言

  • 事务提交:commit;
  • 事务回滚:rollback;

DCL(Data Control Language):数据控制语言

  • 授权:grant
  • 撤销权限:revoke

导入sql脚本:source D:\MySQL\document\bjpowernode.sql

-- ----------------------------
-- Table structure for `dept`
-- ----------------------------
CREATE TABLE `dept` (
  `DEPTNO` int(2) NOT NULL,
  `DNAME` varchar(14) DEFAULT NULL,
  `LOC` varchar(13) DEFAULT NULL,
  PRIMARY KEY (`DEPTNO`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Records of dept
INSERT INTO `dept` VALUES ('10', 'ACCOUNTING', 'NEW YORK');
INSERT INTO `dept` VALUES ('20', 'RESEARCH', 'DALLAS');
INSERT INTO `dept` VALUES ('30', 'SALES', 'CHICAGO');
INSERT INTO `dept` VALUES ('40', 'OPERATIONS', 'BOSTON');

-- ----------------------------
-- Table structure for `emp`
-- ----------------------------
CREATE TABLE `emp` (
  `EMPNO` int(4) NOT NULL,
  `ENAME` varchar(10) DEFAULT NULL,
  `JOB` varchar(9) DEFAULT NULL,
  `MGR` int(4) DEFAULT NULL,
  `HIREDATE` date DEFAULT NULL,
  `SAL` double(7,2) DEFAULT NULL,
  `COMM` double(7,2) DEFAULT NULL,
  `DEPTNO` int(2) DEFAULT NULL,
  PRIMARY KEY (`EMPNO`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Records of emp
INSERT INTO `emp` VALUES ('7369', 'SMITH', 'CLERK', '7902', '1980-12-17', '800.00', null, '20');
INSERT INTO `emp` VALUES ('7499', 'ALLEN', 'SALESMAN', '7698', '1981-02-20', '1600.00', '300.00', '30');
INSERT INTO `emp` VALUES ('7521', 'WARD', 'SALESMAN', '7698', '1981-02-22', '1250.00', '500.00', '30');
INSERT INTO `emp` VALUES ('7566', 'JONES', 'MANAGER', '7839', '1981-04-02', '2975.00', null, '20');
INSERT INTO `emp` VALUES ('7654', 'MARTIN', 'SALESMAN', '7698', '1981-09-28', '1250.00', '1400.00', '30');
INSERT INTO `emp` VALUES ('7698', 'BLAKE', 'MANAGER', '7839', '1981-05-01', '2850.00', null, '30');
INSERT INTO `emp` VALUES ('7782', 'CLARK', 'MANAGER', '7839', '1981-06-09', '2450.00', null, '10');
INSERT INTO `emp` VALUES ('7788', 'SCOTT', 'ANALYST', '7566', '1987-04-19', '3000.00', null, '20');
INSERT INTO `emp` VALUES ('7839', 'KING', 'PRESIDENT', null, '1981-11-17', '5000.00', null, '10');
INSERT INTO `emp` VALUES ('7844', 'TURNER', 'SALESMAN', '7698', '1981-09-08', '1500.00', '0.00', '30');
INSERT INTO `emp` VALUES ('7876', 'ADAMS', 'CLERK', '7788', '1987-05-23', '1100.00', null, '20');
INSERT INTO `emp` VALUES ('7900', 'JAMES', 'CLERK', '7698', '1981-12-03', '950.00', null, '30');
INSERT INTO `emp` VALUES ('7902', 'FORD', 'ANALYST', '7566', '1981-12-03', '3000.00', null, '20');
INSERT INTO `emp` VALUES ('7934', 'MILLER', 'CLERK', '7782', '1982-01-23', '1300.00', null, '10');

-- ----------------------------
-- Table structure for `salgrade`
-- ----------------------------
DROP TABLE IF EXISTS `salgrade`;
CREATE TABLE `salgrade` (
  `GRADE` int(11) DEFAULT NULL,
  `LOSAL` int(11) DEFAULT NULL,
  `HISAL` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Records of salgrade
INSERT INTO `salgrade` VALUES ('1', '700', '1200');
INSERT INTO `salgrade` VALUES ('2', '1201', '1400');
INSERT INTO `salgrade` VALUES ('3', '1401', '2000');
INSERT INTO `salgrade` VALUES ('4', '2001', '3000');
INSERT INTO `salgrade` VALUES ('5', '3001', '9999');

5.简单查询

5.1查询一个字段

select 字段名 from 表名;

select empno from emp -- 从emp表查询empno字段

5.2查询多字段

查询两个字段或者多个字段怎么办?使用逗号隔开“,”

select 字段名,字段名 from 表名;

select empno,ename from emp -- 从emp表查询empno,ename字段

5.3查询所有字段

这种方式的缺点:在实际开发中不建议,可以自己玩没问题。

  • 1、效率低

  • 2、可读性差

select * from 表名

select * from emp -- 从emp表查询所有数据

5.4给查询的列起别名

select 字段名 as 别名 from 表名

select empno,ename as name from emp -- 从emp表查询ename起别名

**注意:**是将显示的查询结果列名显示为name,原表列名还是叫:ename。select语句是永远都不会进行修改操作的。(因为只负责查询)

as关键字可以省略吗?可以的

select empno,ename name from emp

假设起别名的时候,别名里面有空格,怎么办?

select deptno,dname 'dept name' from dept; -- 加单引号
select deptno,dname "dept name" from dept; -- 加双引号

**强调:**数据库中的字符串都是采用单引号括起来。这是标准的。双引号不标准。

5.5计算员工年薪

select ename,sal*12 from emp; -- 结论:字段可以使用数学表达式!
select ename,sal*12 as yearsal from emp; -- 起别名
select ename,sal*12 as '年薪' from emp; -- 别名是中文,用单引号括起来

6.条件查询

6.1什么是条件查询?

不是将表中所有数据都查出来。是查询出来符合条件的。

语法格式:

select
字段1,字段2,字段3…
from
表名
where
条件;

6.2 = 等于

-- 查询薪资等于800的员工姓名和编号?
select empno,ename from emp where sal = 800;
-- 查询SMITH的编号和薪资?
select empno,sal from emp where ename = 'SMITH'; -- 字符串使用单引号

6.3 <>或!= 不等于

-- 查询薪资不等于800的员工姓名和编号?
select empno,ename from emp where sal != 800;
select empno,ename from emp where sal <> 800; -- 小于号和大于号组成的不等号

6.4 < 小于

-- 查询薪资小于2000的员工姓名和编号?
select empno,ename,sal from emp where sal < 2000;

6.5 <= 小于等于

-- 查询薪资小于等于3000的员工姓名和编号?
select empno,ename,sal from emp where sal <= 3000;

6.6 >大于

-- 查询薪资大于3000的员工姓名和编号?
select empno,ename,sal from emp where sal > 3000;

6.7 >= 大于等于

-- 查询薪资大于等于3000的员工姓名和编号?
select empno,ename,sal from emp where sal >= 3000;

6.8 between … and …

between … and ….两个值之间, 等同于 >= and <=

-- 查询薪资在2450和3000之间的员工信息?包括2450和3000
-- 第一种方式:>= and <= (and是并且的意思。)
select empno,ename,sal from emp where sal >= 2450 and sal <= 3000;
-- 第二种方式:between … and …
select empno,ename,sal from emp where sal between 2450 and 3000;

**注意:**使用between and的时候,必须遵循左小右大。between and是闭区间,包括两端的值。

6.9 is null

is null为 null(is not null 不为空)

-- 查询哪些员工的津贴/补助为null?
select empno,ename,sal,comm from emp where comm = null; -- 错误
select empno,ename,sal,comm from emp where comm is null;
-- 查询哪些员工的津贴/补助不为null?
select empno,ename,sal,comm from emp where comm is not null;

**注意:**在数据库当中null不能使用等号进行衡量。需要使用is null,因为数据库中的null代表什么也没有,它不是一个值,所以不能使用等号衡量。

6.10 and 并且

-- 查询工作岗位是MANAGER并且工资大于2500的员工信息?
select empno,ename,job,sal from emp where job = 'MANAGER' and sal > 2500;

6.11 or 或者

-- 查询工作岗位是MANAGER和SALESMAN的员工?
select empno,ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';

6.12 and和or

and和or同时出现的话,有优先级问题吗?

-- 查询工资大于2500,并且部门编号为10或20部门的员工?
select * from emp where sal > 2500 and deptno = 10 or deptno = 20;
--以上这个语句表示什么含义?找出工资大于2500并且部门编号为10的员工,或者20部门所有员工找出来。

注意:and优先级比or高。语句会先执行and,然后执行or。and和or同时出现,and优先级较高。如果想让or先执行,需要加“小括号”,以后在开发中,如果不确定优先级,就加小括号就行了。

select * from emp where sal > 2500 and (deptno = 10 or deptno = 20);

6.13 in

in 包含,相当于多个 or (not in 不在这个范围中)

-- 查询工作岗位是MANAGER和SALESMAN的员工?
select empno,ename,job from emp where job in('MANAGER', 'SALESMAN');

not 可以取非,主要用在 is 或 in 中
is null
is not null
in
not in

注意:in不是一个区间。in后面跟的是具体的值。

6.14 like

-- 称为模糊查询,支持%或下划线匹配
-- %匹配任意多个字符
-- 下划线:任意一个字符。
-- (%是一个特殊的符号,_ 也是一个特殊符号)

-- 找出名字中含有O的?
select ename from emp where ename like '%O%';
-- 找出名字以T结尾的?
select ename from emp where ename like '%T';		
-- 找出名字以K开始的?
select ename from emp where ename like 'K%';
-- 找出第二个字每是A的?
select ename from emp where ename like '_A%';	
-- 找出第三个字母是R的?
select ename from emp where ename like '__R%';

-- 找出名字中有“_”的?
select name from t_student where name like '%_%'; -- 这样不行。
select name from t_student where name like '%\_%'; -- \转义字符。

7.排序 order by

-- 查询所有员工薪资,排序?
select ename,sal from emp order by sal; -- 默认是升序!!!asc
select ename,sal from emp order by sal desc; -- 降序 desc
-- 可以两个字段排序吗?或者说按照多个字段排序?

select ename,sal from emp order by sal asc, ename asc; -- sal在前,起主导,只有sal相等的时候,才会考虑启用ename排序。

-- 找出工资在1250到3000之间的员工信息,要求按照薪资降序排列。
select ename,sal from emp where sal >1250 and sal <3000 order by sal desc;
select ename,sal from emp where sal between 1250 and 3000 order by sal desc;

select

from

where

order by

  以上语句的执行顺序必须掌握:
  	第一步:from
  	第二步:where
  	第三步:select
  	第四步:order by(排序总是在最后执行!)

8.数据处理函数

数据处理函数又被称为单行处理函数,单行处理函数的特点:一个输入对应一个输出。和单行处理函数相对的是:多行处理函数。(多行处理函数特点:多个输入,对应1个输出!)

8.1 lower 转换小写

select lower(ename) as ename from emp;

8.2 upper 转换大写

select upper(name) as name from t_student;

8.3 substr 取子串

-- 找出员工名字第一个字母是A的员工信息?
-- 方式一
select ename from emp where ename like 'A%';
-- 方式二
select ename from emp where substr(ename,1,1) = 'A';

-- 首字母大写?
select concat(upper(substr(name,1,1)),substr(name,2,length(name) - 1)) as result from t_student;

8.4 concat函数

select concat(empno,ename) from emp;

8.5 length 取长度

select length(ename) enamelength from emp;

8.6 trim 去空格

select * from emp where ename = trim(' KING');

8.7 rand() 生成随机数

 select round(rand()*100,0) from emp; -- 100以内的随机数

8.8 ifnull

ifnull是空处理函数。专门处理空的。在所有数据库当中,只要有NULL参与的数学运算,最终结果就是NULL。

-- 计算每个员工的年薪?年薪 = (月薪 + 月补助) * 12
select ename, (sal + ifnull(comm, 0)) * 12 as yearsal from emp;

**注意:**NULL只要参与运算,最终结果一定是NULL。为了避免这个现象,需要使用ifnull函数。ifnull函数用法:ifnull(数据, 被当做哪个值)。如果“数据”为NULL的时候,把这个数据结构当做哪个值。

8.9 其他函数

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;

round 四舍五入

select 'abc' from emp; -- select后面直接跟“字面量/字面值”
+---------+
| abc     |
+---------+
| abc     |
| abc     |
| abc     |
+---------+
select 'abc' as bieming from emp;
+---------+
| bieming |
+---------+
| abc     |
| abc     |
| abc     |
+---------+
select 1000 as num from emp; -- 1000 也是被当做一个字面量/字面值。
+------+
| num  |
+------+
| 1000 |
| 1000 |
| 1000 |
+------+
select round(1236.567, 0) as result from emp; -- 保留整数位
select round(1236.567, 1) as result from emp; -- 保留1个小数
select round(1236.567, 2) as result from emp; -- 保留2个小数
select round(1236.567, -1) as result from emp; -- 保留到十位
select round(1236.567, -2) as result from emp; -- 保留到百位

str_to_date 将字符串转换成日期
date_format 格式化日期
format 设置千分位

-- 在mysql当中怎么计算两个日期的“年差”,差了多少年?
-- TimeStampDiff(间隔类型, 前一个日期, 后一个日期)
-- timestampdiff(YEAR, hiredate, now())

-- 间隔类型:
	-- SECOND   秒,
	-- MINUTE   分钟,
	-- HOUR   小时,
	-- DAY   天,
	-- WEEK   星期
	-- MONTH   月,
	-- QUARTER   季度,
	-- YEAR   年

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

多行处理函数的特点:输入多行,最终输出一行。

count 计数

计算员工数量
select count(ename) from emp;

sum 求和

-- 计算工资和
select sum(sal) from emp;

avg 平均值

-- 计算平均工资
select avg(sal) from emp;

max 最大值

-- 找出最高工资?
select max(sal) from emp;

min 最小值

--找出最低工资?
select min(sal) from emp;

注意:

  • 分组函数在使用的时候必须先进行分组,然后才能用。
  • 如果你没有对数据进行分组,整张表默认为一组。

分组函数在使用的时候需要注意哪些?

  1. 分组函数自动忽略NULL,你不需要提前对NULL进行处理。

  2. 分组函数中count(*)和count(具体字段)有什么区别?

    • count(具体字段):表示统计该字段下所有不为NULL的元素的总数。
    • count(*):统计表当中的总行数。(只要有一行数据count则++),因为每一行记录不可能都为NULL,一行数据中有一列不为NULL,则这行数据就是有效的。
  3. 分组函数不能够直接使用在where子句中。

  4. 所有的分组函数可以组合起来一起用。

10.分组查询

什么是分组查询?

在实际的应用中,可能有这样的需求,需要先进行分组,然后对每一组的数据进行操作。

将之前的关键字全部组合在一起,来看一下他们的执行顺序?

select

from

where

group by

order by

执行顺序是什么?

		1. from
		2. where
		3. group by
		4. select
		5. order by

为什么分组函数不能直接使用在where后面?

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

10.1 group by

-- 案例一
-- 找出每个工作岗位的工资和?
-- 思路:按照工作岗位分组,然后对工资求和。
select job,sum(sal) from emp group by job;
-- 先从emp表中查询数据。
-- 根据job字段进行分组。
-- 然后对每一组的数据进行sum(sal)

-- 案例二
-- 找出每个部门的最高薪资
-- 思路:按照部门编号分组,求每一组的最大值。
select deptno,max(sal) from emp group by deptno;

-- 案例三
-- 找出“每个部门,不同工作岗位”的最高薪资?
select deptno, job, max(sal) from emp group by deptno, job;

10.2 having

使用having可以对分完组之后的数据进一步过滤。having不能单独使用,having不能代替where,having必须和group by联合使用。

-- 找出每个部门最高薪资,要求显示最高薪资大于3000的?
-- 第一步:找出每个部门最高薪资,按照部门编号分组,求每一组最大值。
select deptno,max(sal) from emp group by deptno;
-- 第二步:要求显示最高薪资大于3000
select deptno,max(sal) from emp group by deptno having max(sal) > 3000;

**思考:**以上的sql语句执行效率是不是低?
比较低,实际上可以这样考虑:先将大于3000的都找出来,然后再分组。

select deptno,max(sal) from emp where sal > 3000 group by deptno;

**优化策略:**where和having,优先选择where,where实在完成不了了,再选择having。

-- where没办法的????

-- 找出每个部门平均薪资,要求显示平均薪资高于2500的。
-- 第一步:找出每个部门平均薪资
select deptno,avg(sal) from emp group by deptno;
-- 第二步:要求显示平均薪资高于2500的
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2500;

10.3 单表查询总结

-- SQL语句顺序

select 
		...
from
		...
where
		...
group by
		...
having
		...
order by
		...
-- 以上关键字只能按照这个顺序来,不能颠倒。
-- 执行顺序?

from        -- 从某张表中查询数据,
where       -- 先经过where条件筛选出有价值的数据。
group by    -- 对这些有价值的数据进行分组。
having      -- 分组之后可以使用having继续筛选。
select      -- select查询出来。
order by    -- 最后排序输出!
-- 小案例
-- 找出每个岗位的平均薪资,要求显示平均薪资大于1500的,除MANAGER岗位之外,
-- 要求按照平均薪资降序排。
select 
	job, avg(sal) as avgsal
from
	emp
where
	job <> 'MANAGER'
group by
	job
having
	avg(sal) > 1500
order by
	avgsal desc;

结果去重:distinct

distinct只能出现在所有字段的最前方。

-- distinct出现在job,deptno两个字段之前,表示两个字段联合起来去重。
select distinct job,deptno from emp;

-- 统计一下工作岗位的数量?
select count(distinct job) from emp;

11.连接查询

什么是连接查询?

  • 从一张表中单独查询,称为单表查询。
  • emp表和dept表联合起来查询数据,从emp表中取员工名字,从dept表中取部门名字。
  • 这种跨表查询,多张表联合起来查询数据,被称为连接查询。

连接查询的分类?

  • 根据语法的年代分类:
    • SQL92:1992年的时候出现的语法
    • SQL99:1999年的时候出现的语法(学习)
  • 根据表连接的方式分类:
    • 内连接:
      • 等值连接
      • 非等值连接
      • 自连接
    • 外连接:
      • 左外连接(左连接)
      • 右外连接(右连接
    • 全连接(不讲)

当两张表进行连接查询时,没有任何条件的限制会发生什么现象?

当两张表进行连接查询,没有任何条件限制的时候,最终查询结果条数,是两张表条数的乘积,这种现象被称为:笛卡尔积现象。(笛卡尔发现的,这是一个数学现象。)

关于表起别名

  • 执行效率高
  • 可读性好
-- 案例:查询每个员工所在部门名称?
select e.ename,d.dname from emp e, dept d where e.deptno = d.deptno; -- SQL92语法

**思考:**最终查询的结果条数是14条,但是匹配的过程中,匹配的次数减少了吗?还是56次,只不过进行了四选一。次数没有减少。

注意:通过笛卡尔积现象得出,表的连接次数越多效率越低尽量避免表的连接次数

11.1内连接之等值连接

-- 案例:查询每个员工所在部门名称,显示员工名和部门名?
-- emp e和dept d表进行连接。条件是:e.deptno = d.deptno

-- SQL92语法:
select e.ename,d.dname from emp e, dept d where e.deptno = d.deptno;	
-- sql92的缺点:结构不清晰,表的连接条件,和后期进一步筛选的条件,都放到了where后面。

--SQL99语法:
select e.ename,d.dname from emp e join dept d on e.deptno = d.deptno;
-- inner可以省略(带着inner可读性更好!!!一眼就能看出来是内连接)
select e.ename,d.dname from emp e inner join dept d on e.deptno = d.deptno; 
-- 条件是等量关系,所以被称为等值连接。
-- sql99优点:表连接的条件是独立的,连接之后,如果还需要进一步筛选,再往后继续添加where
-- SQL99语法:

select 
	...
from
	a
join
	b
on
	a和b的连接条件
where
	筛选条件

11.2 内连接之非等值连接

-- 案例:找出每个员工的薪资等级,要求显示员工名、薪资、薪资等级?

select e.ename, e.sal, s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal; 
-- 条件不是一个等量关系,称为非等值连接。

select e.ename, e.sal,s.grade from emp e join salgrade s on e.sal > s.losal
and e.sal < s.hisal;

11.3 内连接之自连接

-- 案例:查询员工的上级领导,要求显示员工名和对应的领导名?
select a.ename '员工' , b.ename '领导' from emp a join emp b on a.mgr = b.empno;
-- 员工的领导编号 = 领导的员工编号
-- 13条记录,没有KING。《内连接》
-- 以上就是内连接中的:自连接,技巧:一张表看做两张表。

11.4 外连接

什么是外连接,和内连接有什么区别?

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

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

  • 带有right的是右外连接,又叫做右连接。

  • 带有left的是左外连接,又叫做左连接。

  • 任何一个右连接都有左连接的写法。

  • 任何一个左连接都有右连接的写法。

-- 案例:查询员工的上级领导,要求显示员工名和对应的领导名?
-- 左连接
select a.ename '员工' , b.ename '领导' from emp a left join emp b on a.mgr = b.empno;

-- 右连接
select a.ename '员工' , b.ename '领导' from emp b right join emp a on a.mgr = b.empno;

-- outer是可以省略的,带着可读性强。
select a.ename '员工' , b.ename '领导' from emp a left outer join emp b on a.mgr = b.empno;

**思考:**外连接的查询结果条数一定是 >= 内连接的查询结果条数?正确。

-- 案例:找出哪个部门没有员工?
select d.* from emp e right join dept d on e.deptno = d.deptno where e.ename is null;

11.5 多表连接查询

-- 语法:
select 
	...
from
	a
join
	b
on
	a和b的连接条件
join
	c
on
	a和c的连接条件
right join
	d
on
	a和d的连接条件
-- 一条SQL中内连接和外连接可以混合。都可以出现!

-- 案例:找出每个员工的部门名称以及工资等级,
-- 要求显示员工名、部门名、薪资、薪资等级
SELECT
	e.ename, d.dname, s.grade 
FROM
	emp e
	JOIN dept d ON e.deptno = d.deptno
	JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal

-- 案例:找出每个员工的部门名称以及工资等级,还有上级领导,
-- 要求显示员工名、领导名、部门名、薪资、薪资等级
SELECT
	e.ename, d.dname, s.grade, b.ename mgr 
FROM
	emp e
	JOIN dept d ON e.deptno = d.deptno
	JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal
	LEFT JOIN emp b ON e.mgr = b.empno

12.子查询

什么是子查询?

select语句中嵌套select语句,被嵌套的select语句称为子查询。

子查询都可以出现在哪里呢?

select
…(select).
from
…(select).
where
…(select).

12.1 where子句中的子查询

-- 案例:找出比最低工资高的员工姓名和工资

-- 实现思路:
-- 第一步:查询最低工资是多少
select min(sal) from emp;
-- 第二步:找出>800的
select ename,sal from emp where sal > 800;
-- 第三步:合并
select ename,sal from emp where sal > (select min(sal) from emp);

12.2 from子句中的子查询

**注意:**from后面的子查询,可以将子查询的查询结果当做一张临时表。(技巧)

--- 案例:找出每个岗位的平均工资的薪资等级。

-- 第一步:找出每个岗位的平均工资(按照岗位分组求平均值)
select job,avg(sal) from emp group by job;
-- 第二步:把以上的查询结果就当做一张真实存在的表t。
SELECT t.*, s.grade 
FROM ( SELECT job, avg( sal ) avgsal FROM emp GROUP BY job ) t
JOIN salgrade s ON t.avgsal BETWEEN s.losal AND s.hisal

12.3 select后面出现的子查询(了解)

-- 案例:找出每个员工的部门名称,要求显示员工名,部门名
SELECT e.ename,( SELECT d.dname FROM dept d WHERE e.deptno = d.deptno ) dname 
FROM emp e;

**注意:**对于select后面的子查询来说,这个子查询只能一次返回1条结果,多于1条,就报错了!

12.4 union合并查询结果集

-- 案例:查询工作岗位是MANAGER和SALESMAN的员工

-- 之前的方法
select ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
select ename,job from emp where job in('MANAGER','SALESMAN');

-- union
select ename,job from emp where job = 'MANAGER'
union
select ename,job from emp where job = 'SALESMAN';

-- union的效率要高一些。
-- 对于表连接来说,每连接一次新表,则匹配的次数满足笛卡尔积,成倍的翻,但是union可以减少匹配的次数。在减少匹配次数的情况下,还可以完成两个结果集的拼接。

-- a 连接 b 连接 c
--   a 10条记录
--   b 10条记录
--   c 10条记录
--   匹配次数是:1000

-- a 连接 b一个结果:10 * 10 --> 100次
-- a 连接 c一个结果:10 * 10 --> 100次
-- 使用union的话是:100次 + 100次 = 200次。(union把乘法变成了加法运算)

union在使用的时候有注意事项吗?

  • union在进行结果集合并的时候,要求两个结果集的列数相同。
-- 错误
select ename,job from emp where job = 'MANAGER'
union
select ename from emp where job = 'SALESMAN';
  • MYSQL可以,oracle语法严格 ,不可以,报错。要求:结果集合并时列和列的数据类型也要一致。
+--------+---------+
| ename  | job     |
+--------+---------+
| JONES  | MANAGER |
| BLAKE  | MANAGER |
| CLARK  | MANAGER |
| ALLEN  | 1600    |
| WARD   | 1250    |
| TURNER | 1500    |
+--------+---------+

12.5 limit(非常重要)

limit作用

将查询结果集的一部分取出来。通常使用在分页查询当中。

分页的作用是为了提高用户的体验,因为一次全部都查出来,用户体验差。
可以一页一页翻页看。

limit怎么用呢?

  • 完整用法:limit startIndex, length
    • startIndex:起始下标
    • 起始下标从0开始。
  • 缺省用法:limit 5; 这是取前5.
-- 按照薪资降序,取出排名在前5名的员工
select ename,sal from emp ORDER BY sal desc limit 5;
select ename,sal from emp ORDER BY sal desc limit 0,5;

注意:mysql当中limit在order by之后执行!!!!!!

-- 取出工资排名在[3-5]名的员工
select ename,sal from emp order by sal desc limit 2, 3;
-- 2表示起始位置从下标2开始,就是第三条记录。
-- 3表示长度。

-- 取出工资排名在[5-9]名的员工
select ename,sal from emp order by sal desc limit 4, 5;

12.6 分页

每页显示3条记录
第1页:limit 0,3
第2页:limit 3,3
第3页:limit 6,3
第4页:limit 9,3

每页显示pageSize条记录
第pageNo页:limit (pageNo - 1) * pageSize , pageSize

public static void main(String[] args){
    // 用户提交过来一个页码,以及每页显示的记录条数
    int pageNo = 5; //第5页
    int pageSize = 10; //每页显示10条

    int startIndex = (pageNo - 1) * pageSize;
    String sql = "select ...limit " + startIndex + ", " + pageSize;
}
// limit (pageNo-1)*pageSize , pageSize

关于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..

13.表的操作

13.1 建表的语法格式

建表的语法格式:(建表属于DDL语句,DDL包括:create drop alter)

create table 表名(字段名1 数据类型, 字段名2 数据类型, 字段名3 数据类型);

create table 表名(
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型
);

表名:建议以t_ 或者 tbl_开始,可读性强。见名知意。
字段名:见名知意。
表名和字段名都属于标识符。

13.2 关于mysql中的数据类型

varchar(最长255)

  • 可变长度的字符串
  • 比较智能,节省空间。
  • 会根据实际的数据长度动态分配空间。
  • 优点:节省空间
  • 缺点:需要动态分配空间,速度慢。

char(最长255)

  • 定长字符串
  • 不管实际的数据长度是多少。
  • 分配固定长度的空间去存储数据。
  • 使用不恰当的时候,可能会导致空间的浪费。
  • 优点:不需要动态分配空间,速度快。
  • 缺点:使用不当可能会导致空间的浪费。

varchar和char我们应该怎么选择?

  • 性别字段你选什么?因为性别是固定长度的字符串,所以选择char。
  • 姓名字段你选什么?每一个人的名字长度不同,所以选择varchar。

int(最长11)

  • 数字中的整数型。等同于java的int。

bigint

  • 数字中的长整型。等同于java中的long。

float

  • 单精度浮点型数据

date

  • 短日期类型

datetime

  • 长日期类型

clob

  • 字符大对象
  • 最多可以存储4G的字符串。
  • 比如:存储一篇文章,存储一个说明。
  • 超过255个字符的都要采用CLOB字符大对象来存储。
  • Character Large OBject:CLOB

blob

  • 二进制大对象
  • Binary Large OBject
  • 专门用来存储图片、声音、视频等流媒体数据。
  • 往BLOB类型的字段上插入数据的时候,例如插入一个图片、视频等,
  • 你需要使用IO流才行。

13.3 创建一个学生表

学号、姓名、年龄、性别、邮箱地址
create table t_student(
no int,
name varchar(32),
sex char(1),
age int(3),
email varchar(255)
);

删除表:drop table t_student; // 当这张表不存在的时候会报错!

// 如果这张表存在的话,删除
drop table if exists t_student;

13.4 插入数据insert(DML)

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

**注意:**字段名和值要一一对应。什么是一一对应?数量要对应。数据类型要对应。

insert into t_student(no,name,sex,age,email) values(1,'zhangsan','m',20,'[email protected]');
insert into t_student(email,name,sex,age,no) values('[email protected]','lisi','f',20,2);
insert into t_student(no) values(3);
insert into t_student(name) values('wangwu');

**注意:**insert语句但凡是执行成功了,那么必然会多一条记录。没有给其它字段指定值的话,默认值是NULL。

insert语句中的“字段名”可以省略吗?

可以

**注意:**前面的字段名省略的话,等于都写上了!所以值也要都写上!

insert into t_student values(2, 'lisi', 'f', 20, '[email protected]');

13.5 insert插入日期

数字格式化:format(数字, ‘格式’)

str_to_date:将字符串varchar类型转换成date类型

str_to_date('字符串日期', '日期格式')
-- mysql的日期格式:
%Y	年
%m  月
%d  日
%h	时
%i	分
%s	秒
-- str_to_date函数可以把字符串varchar转换成日期date类型数据,
-- 通常使用在插入insert方面,因为插入的时候需要一个日期类型的数据,
-- 需要通过该函数将字符串转换成date。

-- 如果你提供的日期字符串是这个格式,str_to_date函数就不需要了!!!
-- %Y-%m-%d
insert into t_user(id,name,birth) values(2, 'lisi', '1990-10-01');

date_format:将date类型转换成具有一定格式的varchar字符串类型。

date_format(日期类型数据, '日期格式')

-- date_format 这个函数可以将日期类型转换成特定格式的字符串。
select id,name,date_format(birth, '%m/%d/%Y') as birth from t_user;

date和datetime两个类型的区别?

date是短日期:只包括年月日信息。
mysql短日期默认格式:%Y-%m-%d

datetime是长日期:包括年月日时分秒信息。
mysql长日期默认格式:%Y-%m-%d %h:%i:%s

在mysql当中怎么获取系统当前时间?

now() 函数,并且获取的时间带有:时分秒信息!!!!是datetime类型的。

13.6 修改update(DML)

**语法格式:**update 表名 set 字段名1=值1,字段名2=值2,字段名3=值3… where 条件;

update t_user set name = 'jack', birth = '2000-10-11' where id = 2;
update t_user set name = 'jack', birth = '2000-10-11', create_time = now() where id = 2;

**注意:**没有条件限制会导致所有数据全部更新。

update t_user set name = 'abc';

13.7 删除数据 delete (DML)

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

delete from t_user where id = 2;

**注意:**没有条件,整张表的数据会全部删除!

delete from t_user; -- 删除所有!

13.8 插入多条数据

insert语句可以一次插入多条记录吗?

可以的!

**语法:**insert into t_user(字段名1,字段名2) values(),(),(),();

insert into t_user(id,name,birth,create_time) values
(1,'zs','1980-10-11',now()), 
(2,'lisi','1981-10-11',now()),
(3,'wangwu','1982-10-11',now());

13.9 快速创建表(了解)

create table emp2 as select * from emp;
-- 原理:
-- 将一个查询结果当做一张表新建!!!!!
-- 这个可以完成表的快速复制!!!!
-- 表创建出来,同时表中的数据也存在了!!!

13.10 将查询结果插入到一张表中(了解)

insert into dept_bak select * from dept; -- 很少用!

13.11 快速删除表中的数据(掌握)

-- 删除dept_bak表中的数据
-- 这种删除数据的方式比较慢
delete from dept_bak;

-- delete语句删除数据的原理?(delete属于DML语句!!!)
	-- 表中的数据被删除了,但是这个数据在硬盘上的真实存储空间不会被释放!!!
	-- 这种删除缺点是:删除效率比较低。
	-- 这种删除优点是:支持回滚,后悔了可以再恢复数据!!!

-- truncate语句删除数据的原理?
	-- 这种删除效率比较高,表被一次截断,物理删除。
	-- 这种删除缺点:不支持回滚。
	-- 这种删除优点:快速。
	
-- 用法: (这种操作属于DDL操作。)
truncate table dept_bak;

-- 大表非常大,上亿条记录????
	-- 删除的时候,使用delete,也许需要执行1个小时才能删除完!效率较低。
	-- 可以选择使用truncate删除表中的数据。只需要不到1秒钟的时间就删除结束。效率较高。
	-- 但是使用truncate之前,必须仔细询问客户是否真的要删除,并警告删除之后不可恢复!

	-- truncate是删除表中的数据,表还在!

13.12 对表结构的增删改

什么是对表结构的修改?

添加一个字段,删除一个字段,修改一个字段!!!

对表结构的修改需要使用:alter 属于DDL语句

  • 在实际的开发中,需求一旦确定之后,表一旦设计好之后,很少的进行表结构的修改。因为开发进行中的时候,修改表结构,成本比较高。修改表的结构,对应的java代码就需要进行大量的修改。成本是比较高的。这个责任应该由设计人员来承担!
  • 由于修改表结构的操作很少,所以我们不需要掌握,如果有一天真的要修改表结构,你可以使用工具!!!!
  • 修改表结构的操作是不需要写到java程序中的。实际上也不是java程序员的范畴。

14.约束(重要)

什么是约束?

  • 约束对应的英语单词:constraint

  • 在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的

  • 完整性、有效性!!!

  • 约束的作用就是为了保证:表中的数据有效!!

约束包括哪些?

  • 非空约束:not null(学习)
  • 唯一性约束: unique(学习)
  • 主键约束: primary key (简称PK)(学习)
  • 外键约束:foreign key(简称FK)(学习)
  • 检查约束:check(mysql不支持,oracle支持)

14.1 非空约束

非空约束not null约束的字段不能为NULL。

create table t_vip(
    id int,
    name varchar(255) not null  -- not null只有列级约束,没有表级约束!
);

14.2 唯一性约束

唯一性约束unique约束的字段不能重复,但是可以为NULL。

-- name具有唯一性
create table t_vip(
    id int,
    name varchar(255) unique,
    email varchar(255)
);

-- name和email两个字段联合起来具有唯一性
create table t_vip(
    id int,
    name varchar(255),
    email varchar(255),
    unique(name,email) -- 约束没有添加在列的后面,这种约束被称为表级约束。
);

-- 什么时候使用表级约束呢?
	-- 需要给多个字段联合起来添加某一个约束的时候,需要使用表级约束。

-- unique 和not null可以联合吗?
create table t_vip(
    id int,
    name varchar(255) not null unique
);
--在mysql当中,如果一个字段同时被not null和unique约束的话,该字段自动变成主键字段。(注意:oracle中不一样!)

14.3 主键约束

主键约束(primary key,简称PK)

主键约束的相关术语?

  • 主键约束:就是一种约束。
  • 主键字段:该字段上添加了主键约束,这样的字段叫做:主键字段
  • 主键值:主键字段中的每一个值都叫做:主键值。

什么是主键?有啥用?

  • 主键值是每一行记录的唯一标识。
  • 主键值是每一行记录的身份证号!!!

**记住:**任何一张表都应该有主键,没有主键,表无效!!

**主键的特征:**not null + unique(主键值不能是NULL,同时也不能重复!)

怎么给一张表添加主键约束呢?

-- 列级约束
create table t_vip(
    id int primary key,  
    name varchar(255)
);

-- 表级约束
create table t_vip(
    id int,
    name varchar(255),
    primary key(id)  
);

-- 复合主键
create table t_vip(
    id int,
    name varchar(255),
    email varchar(255),
    primary key(id,name)
);

-- 在实际开发中不建议使用:复合主键。建议使用单一主键!
-- 因为主键值存在的意义就是这行记录的身份证号,只要意义达到即可,单一主键可以做到。
-- 复合主键比较复杂,不建议使用!!!

-- 主键值建议使用:
		int
		bigint
		char
		...
-- 不建议使用:varchar来做主键。主键值一般都是数字,一般都是定长的!

一个表中主键约束能加两个吗?

一张表,主键约束只能添加1个。(主键只能有1个。)

单一主键和复合主键之外,还可以这样进行分类?

  • 自然主键:主键值是一个自然数,和业务没关系。
  • 业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值。这就是业务主键!

在实际开发中使用业务主键多,还是使用自然主键多一些?

  • 自然主键使用比较多,因为主键只要做到不重复就行,不需要有意义。
  • 业务主键不好,因为主键一旦和业务挂钩,那么当业务发生变动的时候,可能会影响到主键值,所以业务主键不建议使用。尽量使用自然主键。

在mysql当中,有一种机制,可以帮助我们自动维护一个主键值?

auto_increment表示自增,从1开始,以1递增!

14.4 外键约束

外键约束(foreign key,简称FK)

外键约束涉及到的相关术语:

  • 外键约束:一种约束(foreign key)
  • 外键字段:该字段上添加了外键约束
  • 外键值:外键字段当中的每一个值。
drop table if exists t_student;
drop table if exists t_class;

create table t_class(
	cno int,
    cname varchar(255),
    primary key(cno)
);

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

insert into t_class values(101,'一班');
insert into t_class values(102,'二班');

insert into t_student values(001,'张三',101);
insert into t_student values(002,'张四',101);
insert into t_student values(003,'张五',102);
insert into t_student values(004,'张六',102);

删除表的顺序?先删子,再删父。

创建表的顺序?先创建父,再创建子。

删除数据的顺序?先删子,再删父。

插入数据的顺序?先插入父,再插入子。

思考:子表中的外键引用的父表中的某个字段,被引用的这个字段必须是主键吗?

不一定是主键,但至少具有unique约束。

外键可以为NULL吗?

外键值可以为NULL。

15.存储引擎

什么是存储引擎,有什么用呢?

  • 存储引擎是MySQL中特有的一个术语,其它数据库中没有。(Oracle中有,但是不叫这个名字)
  • 实际上存储引擎是一个表存储/组织数据的方式。
  • 不同的存储引擎,表存储数据的方式不同。

怎么给表添加/指定“存储引擎”呢?

-- 可以在建表的时候给表指定存储引擎。
CREATE TABLE `t_student` (
    `no` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(255) DEFAULT NULL,
    `cno` int(11) DEFAULT NULL,
    PRIMARY KEY (`no`),
    KEY `cno` (`cno`),
    CONSTRAINT `t_student_ibfk_1` FOREIGN KEY (`cno`) REFERENCES `t_class` (`classno`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

-- 在建表的时候可以在最后小括号的")"的右边使用:
	-- ENGINE来指定存储引擎。
	-- CHARSET来指定这张表的字符编码方式。
	
-- mysql默认的存储引擎是:InnoDB
-- mysql默认的字符编码方式是:utf8

怎么查看mysql支持哪些存储引擎呢?

show engines \G

15.1 MyISAM存储引擎

它管理的表具有以下特征:

  • 使用三个文件表示每个表:

    • 格式文件 — 存储表结构的定义(mytable.frm)
    • 数据文件 — 存储表行的内容(mytable.MYD)
    • 索引文件 — 存储表上索引(mytable.MYI):索引是一本书的目录,缩小扫描范围,提高查询效率的一种机制。
  • 可被转换为压缩、只读表来节省空间

  • 对于一张表来说,只要是主键,或者加有unique约束的字段上会自动创建索引。

  • MyISAM存储引擎特点:

    • 可被转换为压缩、只读表来节省空间
  • MyISAM不支持事务机制,安全性低。

15.2 InnoDB存储引擎

  • 这是mysql默认的存储引擎,同时也是一个重量级的存储引擎。

  • InnoDB支持事务,支持数据库崩溃后自动恢复机制。

  • InnoDB存储引擎最主要的特点是:非常安全。

  • 它管理的表具有下列主要特征:

    • 每个 InnoDB 表在数据库目录中以.frm 格式文件表示
    • InnoDB 表空间 tablespace 被用于存储表的内容(表空间是一个逻辑名称。表空间存储数据+索引。)
    • 提供一组用来记录事务性活动的日志文件
    • 用 COMMIT(提交)、SAVEPOINT 及ROLLBACK(回滚)支持事务处理
    • 提供全 ACID 兼容
    • 在 MySQL 服务器崩溃后提供自动恢复
    • 多版本(MVCC)和行级锁定
    • 支持外键及引用的完整性,包括级联删除和更新
  • InnoDB最大的特点就是支持事务:

    • 以保证数据的安全。效率不是很高,并且也不能压缩,不能转换为只读,不能很好的节省存储空间。

15.3 MEMORY存储引擎

  • 使用 MEMORY 存储引擎的表,其数据存储在内存中,且行的长度固定,

  • 这两个特点使得 MEMORY 存储引擎非常快。

  • MEMORY 存储引擎管理的表具有下列特征:

    • 在数据库目录内,每个表均以.frm 格式的文件表示。
    • 表数据及索引被存储在内存中。(目的就是快,查询快!)
    • 表级锁机制。
    • 不能包含 TEXT 或 BLOB 字段。
  • MEMORY引擎优点:查询效率是最高的。不需要和硬盘交互。

  • MEMORY引擎缺点:不安全,关机之后数据消失。因为数据和索引都是在内存当中。

16.事务

什么是事务?

一个事务其实就是一个完整的业务逻辑。是一个最小的工作单元。不可再分。

只有DML语句才会有事务这一说,其它语句和事务无关!!!

insert
delete
update

因为 只有以上的三个语句是数据库表中数据进行增、删、改的。
只要你的操作一旦涉及到数据的增、删、改,那么就一定要考虑安全问题。

假设所有的业务,只要一条DML语句就能完成,还有必要存在事务机制吗?

正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事儿都能一条DML语句搞定,那么事务则没有存在的价值了。

事务是怎么做到多条DML语句同时成功和同时失败的呢?

InnoDB存储引擎:提供一组用来记录事务性活动的日志文件,在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。在事务的执行过程中,我们可以提交事务,也可以回滚事务。

提交事务

清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。提交事务标志着,事务的结束。并且是一种全部成功的结束。

回滚事务

将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件,回滚事务标志着,事务的结束。并且是一种全部失败的结束。

怎么提交事务,怎么回滚事务?

  • 提交事务:commit; 语句

  • 回滚事务:rollback; 语句(回滚永远都是只能回滚到上一次的提交点!)

在mysql当中默认的事务行为是怎样的?

mysql默认情况下是支持自动提交事务的。(自动提交)每执行一条DML语句,则提交一次!

这种自动提交实际上是不符合我们的开发习惯,因为一个业务通常是需要多条DML语句共同执行才能完成的,为了保证数据的安全,必须要求同时成功之后再提交,所以不能执行一条就提交一条。

怎么将mysql的自动提交机制关闭掉呢?

-- 先执行这个命令:
start transaction;

事务包括4个特性

  • A:原子性

    • 说明事务是最小的工作单元。不可再分。
  • C:一致性

    • 所有事务要求,在同一个事务当中,所有操作必须同时成功,或者同时失败,以保证数据的一致性。
  • I:隔离性

    • A事务和B事务之间具有一定的隔离。
    • 教室A和教室B之间有一道墙,这道墙就是隔离性。
    • A事务在操作一张表的时候,另一个事务B也操作这张表会那样???
  • D:持久性

    • 事务最终结束的一个保障。事务提交,就相当于将没有保存到硬盘上的数据保存到硬盘上!

重点研究一下事务的隔离性!!!

  • 读未提交:read uncommitted(最低的隔离级别)《没有提交就读到了》

什么是读未提交?
事务A可以读取到事务B未提交的数据。

这种隔离级别存在的问题就是:
脏读现象!(Dirty Read),我们称读到了脏数据。

这种隔离级别一般都是理论上的,大多数的数据库隔离级别都是二档起步!

  • 读已提交:read committed《提交之后才能读到》

什么是读已提交?
​ 事务A只能读取到事务B提交之后的数据。

这种隔离级别解决了什么问题?
​ 解决了脏读的现象。

这种隔离级别存在什么问题?
​ 不可重复读取数据。

什么是不可重复读取数据呢?
在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读取。

这种隔离级别是比较真实的数据,每一次读到的数据是绝对的真实。
oracle数据库默认的隔离级别是:read committed

  • 可重复读:repeatable read《提交之后也读不到,永远读取的都是刚开启事务时的数据》

什么是可重复读取?
​ 事务A开启之后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。

可重复读解决了什么问题?
​ 解决了不可重复读取数据。

可重复读存在的问题是什么?
​ 可以会出现幻影读。每一次读取到的数据都是幻象。不够真实!

mysql中默认的事务隔离级别就是这个

  • 序列化/串行化:serializable(最高的隔离级别)

这是最高隔离级别,效率最低。解决了所有的问题。
这种隔离级别表示事务排队,不能并发!
synchronized,线程同步(事务同步)
每一次读取到的数据都是最真实的,并且效率是最低的。

17.索引

什么是索引?

索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制。一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。

MySQL在查询方面主要就是两种方式:

  • 第一种方式:全表扫描
  • 第二种方式:根据索引检索。

在mysql数据库当中索引也是需要排序的,并且这个所以的排序和TreeSet数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树!在mysql当中索引是一个B-Tree数据结构。

遵循左小右大原则存放。采用中序遍历方式遍历取数据。

索引的实现原理?

  • 在任何数据库当中主键上都会自动添加索引对象,id字段上自动有索引,因为id是PK。另外在mysql当中,一个字段上如果有unique约束的话,也会自动创建索引对象。

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

  • 在mysql当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在,在MyISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中索引存储在一个逻辑名称叫做tablespace的当中。在MEMORY存储引擎当中索引被存储在内存当中。不管索引存储在哪里,索引在mysql当中都是一个树的形式存在。(自平衡二叉树:B-Tree)

在mysql当中,主键上,以及unique字段上都会自动添加索引的!!!!

什么条件下,我们会考虑给字段添加索引呢?

  • 数据量庞大(到底有多么庞大算庞大,这个需要测试,因为每一个硬件环境不同)

  • 该字段经常出现在where的后面,以条件的形式存在,也就是说这个字段总是被扫描。

  • 该字段很少的DML(insert delete update)操作。(因为DML之后,索引需要重新排序。)

建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。
建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的。

索引怎么创建?怎么删除?语法是什么?

  • 创建索引:
-- 给emp表的ename字段添加索引,起名:emp_ename_index
create index emp_ename_index on emp(ename);
  • 删除索引:
-- 将emp表上的emp_ename_index索引对象删除。
drop index emp_ename_index on emp;

在mysql当中,怎么查看一个SQL语句是否使用了索引进行检索?

explain select * from emp where ename = 'KING';
-- type=ALL,扫描14条记录(扫描了全部表的数据),说明没有使用索引。

create index emp_ename_index on emp(ename); -- 创建索引对象。

explain select * from emp where ename = 'KING';
-- type=ref,说明有使用了索引,可以发现只扫描了1条记录

索引有失效的时候,什么时候索引失效呢?

-- 失效的第1种情况:
select * from emp where ename like '%T'; -- 索引失效
-- ename上即使添加了索引,也不会走索引,为什么?
	-- 原因是因为模糊匹配当中以“%”开头了!
	-- 尽量避免模糊查询的时候以“%”开始。
	-- 这是一种优化的手段/策略。
	
-- 失效的第2种情况:
select * from emp where ename = 'KING' or job = 'MANAGER'; -- 索引失效
-- 使用or的时候会失效,如果使用or那么要求or两边的条件字段都要有索引,才会走索引,如果其中一边有一个字段没有索引,那么另一个字段上的索引也会失效。所以这就是为什么不建议使用or的原因。

-- 失效的第3种情况:
-- 使用复合索引的时候,没有使用左侧的列查找,索引失效
--什么是复合索引?
	-- 两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引。
create index emp_job_sal_index on emp(job,sal);
explain select * from emp where job = 'MANAGER'; -- 索引有效
explain select * from emp where sal = 800; -- 索引失效

-- 失效的第4种情况:
-- 在where当中索引列参加了运算,索引失效。
create index emp_sal_index on emp(sal);
explain select * from emp where sal = 800; -- 索引有效
explain select * from emp where sal+1 = 800; -- 索引失效

-- 失效的第5种情况:
-- 在where当中索引列使用了函数
explain select * from emp where lower(ename) = 'smith'; -- 索引失效

-- 还有很多

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

索引在数据库当中分了很多类?

  • 单一索引:一个字段上添加索引。
  • 复合索引:两个字段或者更多的字段上添加索引。
  • 主键索引:主键上添加索引。
  • 唯一性索引:具有unique约束的字段上添加索引。

注意:唯一性比较弱的字段上添加索引用处不大。

18.视图

什么是视图?

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

怎么创建视图对象?怎么删除视图对象?

-- 创建视图对象
create view dept2_view as select * from dept2;
-- 删除视图对象
drop view dept2_view;

**注意:**只有DQL语句才能以view的形式创建。

create view view_name as 这里的语句必须是DQL语句;

用视图做什么?

我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作!(视图的特点:通过对视图的操作,会影响到原表数据。)

-- 面向视图查询
select * from dept2_view; 

-- 面向视图插入
insert into dept2_view(deptno,dname,loc) values(60,'SALES', 'BEIJING');

-- 面向视图删除
delete from dept2_view;

-- 面向视图更新
update emp_dept_view set sal = 1000 where dname = 'ACCOUNTING';

我们以后面向视图开发的时候,使用视图的时候可以像使用table一样。可以对视图进行增删改查等操作。视图不是在内存当中,视图对象也是存储在硬盘上的,不会消失。

C:Create(增)
R:Retrive(查:检索)
U:Update(改)
D:Delete(删)

19.数据库设计三范式

什么是数据库设计范式?

数据库表的设计依据。教你怎么进行数据库表的设计。

数据库设计范式共有?

第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。

  • 最核心,最重要的范式,所有表的设计都需要满足。必须有主键,并且每一个字段都是原子性不可再分。

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

  • 建立在第一范式的基础之上,要求所有非主键字段必须完全依赖主键,不要产生部分依赖。

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

  • 第三范式建立在第二范式的基础之上要求所有非主键字典必须直接依赖主键,不要产生传递依赖。

**声明:**三范式是面试官经常问的,所以一定要熟记在心!

设计数据库表的时候,按照以上的范式进行,可以避免表中数据的冗余,空间的浪费。

提醒:

  • 数据库设计三范式是理论上的。
  • 实践和理论有的时候有偏差。
  • 最终的目的都是为了满足客户的需求,有的时候会拿冗余换执行速度。

34道作业题

-- 连接查询
select 
	...
from
	a
join
	b
on
	a和b的连接条件
where
	筛选条件
	
	
-- 分组查询
select
			...
from
			...
where
			...
group by
			...
order by
			...
-- 执行顺序
		1. from
		2. where
		3. group by
		4. select
		5. order by			

-- 结合查询样式
select 
	...
from
	a
----------------------
join
	b
on
	a和b的连接条件
... 多个join...
----------------------
where
	筛选条件
group by
			...
order by
			...

1、取得每个部门最高薪水的人员名称

-- 第一步:取得每个部门最高薪水(按照部门编号分组,找出每一组最大值)
SELECT deptno, max( sal ) maxsal FROM emp GROUP BY deptno;
-- 第二步:将以上的查询结果当做一张临时表t
SELECT e.ename, t.* FROM emp e 
JOIN ( SELECT deptno, max( sal ) maxsal FROM emp GROUP BY deptno ) t 
ON e.sal = t.maxsal;

2、哪些人的薪水在部门的平均薪水之上

-- 第一步:查询每个部门的平均薪水
select deptno, avg(sal) avgsal from emp group by deptno;
-- 第二步:部门标号相同,且大于平均薪水
SELECT e.ename, e.sal, t.* 
FROM emp e
JOIN ( SELECT deptno, avg( sal ) avgsal FROM emp GROUP BY deptno ) t 
ON e.deptno = t.deptno and	e.sal > t.avgsal;

3、取得部门中(所有人的)平均的薪水等级

-- 第一步:先查询每个人的薪水等级
SELECT e.deptno, e.sal, s.grade 
FROM emp e
JOIN salgrade s 
ON e.sal BETWEEN s.losal AND s.hisal;
-- 第二步:根据部门分组
SELECT e.deptno, e.sal, avg( s.grade ) 
FROM emp e
JOIN salgrade s 
ON e.sal BETWEEN s.losal AND s.hisal 
GROUP BY e.deptno;

4、不准用组函数(Max ),取得最高薪水

-- 降序排列 
select ename ,sal from emp order by sal desc limit 1;
-- max函数
select ename ,max(sal) from emp;
-- 表自联
SELECT sal FROM emp WHERE sal NOT IN
(SELECT DISTINCT a.sal 
 FROM emp a 
 JOIN emp b 
 ON a.sal < b.sal);

5、取得平均薪水最高的部门的部门编号

-- 倒序方式
-- 第一步:求出每个部门平均薪水
SELECT deptno, avg(sal)  FROM emp GROUP BY deptno;
-- 第二步:倒序第一个
select t.* from
(select deptno,avg(sal) avgsal from emp group by deptno) t
order by t.avgsal desc limit 1;

--max方式
-- 第一步:求出每个部门平均薪水
SELECT deptno, avg(sal) avgsal  FROM emp GROUP BY deptno;
-- 第二步:求出max
select t.deptno ,max(t.avgsal) from
(select deptno,avg(sal) avgsal from emp group by deptno) t
-- 第二步:和max对比
SELECT deptno, avg(sal) avgsal  FROM emp GROUP BY deptno
having avgsal = (select max(t.avgsal) from
(select deptno,avg(sal) avgsal from emp group by deptno) t)

6、取得平均薪水最高的部门的部门名称

SELECT d.dname, avg( e.sal ) avgsal 
FROM emp e
	JOIN dept d ON e.deptno = d.deptno 
GROUP BY
	d.dname 
ORDER BY
	avgsal DESC 
	LIMIT 1

7、求平均薪水的等级最低的部门的部门名称

-- 第一步:求每个部门的平均薪水
SELECT d.dname, avg( e.sal ) avgsal 
FROM emp e JOIN dept d ON e.deptno = d.deptno GROUP BY d.dname;
-- 第二步:求每个部门的平均薪水的等级
SELECT t.*, s.grade 
FROM salgrade s RIGHT JOIN (
    SELECT d.dname, avg( e.sal ) avgsal 
    FROM emp e
    JOIN dept d ON e.deptno = d.deptno 
    GROUP BY d.dname) t 
ON t.avgsal BETWEEN s.losal AND s.hisal;
-- 第三步:求出薪水最低的等级
SELECT s.grade 
FROM salgrade s RIGHT JOIN (
    SELECT d.dname, avg( e.sal ) avgsal 
    FROM emp e
    JOIN dept d ON e.deptno = d.deptno 
    GROUP BY d.dname 
) t ON t.avgsal BETWEEN s.losal AND s.hisal 
ORDER BY s.grade LIMIT 1;
-- 第四步:匹配最低的等级
SELECT
	t.*, s.grade 
FROM
	salgrade s RIGHT JOIN (
        SELECT d.dname, avg( e.sal ) avgsal 
        FROM emp e
        JOIN dept d ON e.deptno = d.deptno 
        GROUP BY d.dname 
    ) t ON t.avgsal BETWEEN s.losal 
	AND s.hisal 
WHERE
	s.grade = (
        SELECT s.grade 
        FROM salgrade s
        RIGHT JOIN (
            SELECT d.dname, avg( e.sal ) avgsal 
            FROM emp e JOIN dept d ON e.deptno = d.deptno 
            GROUP BY d.dname 
        ) t ON t.avgsal BETWEEN s.losal 
        AND s.hisal 
        ORDER BY s.grade  LIMIT 1 
    );

8、取得比普通员工(员工代码没有在 mgr 字段上出现的) 的最高薪水还要高的领导人姓名

-- 第一步:找出领导
select distinct mgr from emp where mgr is not null;
-- 第二步:找出普通员工最大薪水
select max(sal) from emp where empno 
not in(select distinct mgr from emp where mgr is not null)
-- 第三步:找出大于普通员工最大薪水的人
SELECT ename, sal FROM emp WHERE sal >(
    SELECT max( sal ) 
    FROM emp 
    WHERE empno NOT IN ( SELECT DISTINCT mgr FROM emp WHERE mgr IS NOT NULL ));

9、取得薪水最高的前五名员工

select ename ,sal from emp order by sal desc limit 5;

10、取得薪水最高的第六到第十名员工

select ename ,sal from emp order by sal desc limit 5,5;

11、取得最后入职的 5 名员工

select ename ,HIREDATE  from emp order by hiredate desc limit 5;

12、取得每个薪水等级有多少员工

-- 第一步:查询每一个员工的薪水等级
SELECT e.ename, s.grade FROM emp e JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal;
-- 第一步:统计每个薪水等级人数
SELECT s.grade, count( e.ename ) count 
FROM emp e JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal 
GROUP BY s.grade;

13、面试题:
有 3 个表 S(学生表),C(课程表),SC(学生选课表)
S(SNO,SNAME)代表(学号,姓名)
C(CNO,CNAME,CTEACHER)代表(课号,课名,教师)
SC(SNO,CNO,SCGRADE)代表(学号,课号,成绩)
问题:
1,找出没选过“黎明”老师的所有学生姓名。
2,列出 2 门以上(含2 门)不及格学生姓名及平均成绩。
3,即学过 1 号课程又学过 2 号课所有学生的姓名。

-- 练习自行创建三张表 --

-- 第一题:找出没选过“黎明”老师的所有学生姓名。

-- 第一步:找出黎明老师的课程号
SELECT cno FROM c WHERE cteacher = '黎明';
-- 第二步:找出选择黎明老师的学生号
SELECT DISTINCT sno FROM sc WHERE cno = ( SELECT cno FROM c WHERE cteacher = '黎明' );
-- 第三步:查找除了选择黎明老师的的学生姓名 
SELECT sname FROM s WHERE sno <> (SELECT DISTINCT sno FROM sc WHERE cno = (SELECT cno FROM c WHERE cteacher = '黎明' ));

-- 第二题:列出 2 门以上(含2 门)不及格学生姓名及平均成绩。

-- 第一步:查出不及格成绩的学号
select sno from sc where scgrade < 60;
-- 第二步:按学号分组,查出不及格个数和平均成绩
select sno, avg(scgrade) avg ,count(scgrade) count from sc where scgrade < 60 group by sno;
-- 第二步:查出2门以上(含2门)不及格学生姓名及平均成绩
SELECT s.sname, t.avg FROM s s 
JOIN ( SELECT sno, avg( scgrade ) avg, count( scgrade ) count FROM sc WHERE scgrade < 60 GROUP BY sno ) t ON t.sno = s.sno AND t.count >= 2;

-- 第三题:即学过 1 号课程又学过 2 号课所有学生的姓名。

-- 第一步:在成绩表查出这两门课有成绩的记录
select sno from sc where cno in (981201,981202);
-- 第二步:根据学号分组,计算个数	
select sno,count(sno) count from sc where cno in (981201,981202) group by sno ;
-- 第三步:查出姓名
SELECT s.sname FROM s s JOIN ( SELECT sno, count( sno ) count FROM sc WHERE cno IN ( 981201, 981202 ) GROUP BY sno ) t ON s.sno = t.sno AND t.count = 2;

14、列出所有员工及领导的姓名

SELECT a.ename '员工', b.ename '领导' FROM emp a LEFT JOIN emp b ON a.mgr = b.empno

15、列出受雇日期早于其直接上级的所有员工的编号,姓名,部门名称

-- 三表联查

SELECT
	a.empno,
	a.ename '员工',
	a.hiredate,
	b.ename '领导',
	b.hiredate,
	d.dname 
FROM emp a
	JOIN emp b ON a.mgr = b.empno AND a.hiredate < b.hiredate
	JOIN dept d ON a.deptno = d.deptno;

16、 列出部门名称和这些部门的员工信息, 同时列出那些没有员工的部门

SELECT
	e.*,
	d.dname 
FROM
	emp e
	RIGHT JOIN dept d ON e.deptno = d.deptno;

17、列出至少有 5 个员工的所有部门

-- 第一步:查询员工部门
SELECT e.* ,d.dname FROM emp e RIGHT JOIN dept d ON e.deptno = d.deptno;
-- 第二步:至少有 5 个员工的所有部门
-- 方式一
SELECT t.* 
FROM
	( SELECT d.dname, count( d.dname ) count 
	FROM emp e
		RIGHT JOIN dept d ON e.deptno = d.deptno 
	GROUP BY d.dname ) t 
WHERE
	t.count >=5;
	
-- 注意:GROUP BY 中 使用 where 获取不到 count函数 可以使用 having 进行筛选
--方式二
SELECT d.dname, count( d.dname ) count 
FROM emp e
	RIGHT JOIN dept d ON e.deptno = d.deptno 
GROUP BY d.dname 
HAVING count >=5;

18、列出薪金比"SMITH" 多的所有员工信息

-- 第一步:查询出"SMITH"薪水
select sal from emp where ename = 'SMITH';
-- 第二步:查出薪水比"SMITH" 多的所有员工
select * from emp where sal > (select sal from emp where ename = 'SMITH');

19、 列出所有"CLERK"( 办事员) 的姓名及其部门名称, 部门的人数

-- "CLERK"( 办事员) 的姓名及其部门名称
SELECT e.ename, d.dname 
FROM emp e
	JOIN dept d ON e.deptno = d.deptno 
WHERE e.job = 'CLERK';

-- 部门的人数
SELECT e.ename, t.* 
FROM emp e
	JOIN dept d ON e.deptno = d.deptno
	JOIN ( SELECT deptno, count( deptno ) count FROM emp GROUP BY deptno ) t 
	ON e.deptno = t.deptno 
WHERE e.job = 'CLERK';

20、列出最低薪金大于 1500 的各种工作及从事此工作的全部雇员人数

select job,count(*) from emp group by job having min(sal) > 1500;

21、列出在部门"SALES"< 销售部> 工作的员工的姓名, 假定不知道销售部的部门编号

select ename from emp where deptno = (select deptno from dept where dname = 'SALES');

22、列出薪金高于公司平均薪金的所有员工, 所在部门, 上级领导, 雇员的工资等级.

SELECT
	e.ename '员工',
	d.dname,
	b.ename '领导',
	s.grade 
FROM
	emp e
	JOIN dept d ON e.deptno = d.deptno
	LEFT JOIN emp b ON e.mgr = b.empno
	JOIN salgrade s ON e.sal BETWEEN s.losal 
	AND s.hisal 
WHERE
	e.sal > (
	SELECT
		avg( sal ) 
	FROM
	emp);

23、 列出与"SCOTT" 从事相同工作的所有员工及部门名称

SELECT
	e.ename,
	e.job,
	d.dname 
FROM
	emp e
	JOIN dept d ON e.deptno = d.deptno 
WHERE
	e.job = ( SELECT job FROM emp WHERE ename = 'SCOTT' ) 
	AND e.ename <> 'SCOTT';

24、列出薪金等于部门 30 中员工的薪金的其他员工的姓名和薪金

SELECT
	ename,
	sal 
FROM
	emp 
WHERE
	sal IN ( SELECT sal FROM emp WHERE deptno = 30 ) 
	AND deptno <> 30;

25、列出薪金高于在部门 30 工作的所有员工的薪金的员工姓名和薪金、部门名称

SELECT
	e.ename,
	e.sal,
	d.dname 
FROM
	emp e
	JOIN dept d ON e.deptno = d.deptno 
WHERE
	e.sal > ( SELECT max( sal ) FROM emp WHERE deptno = 30 ) 
	AND e.deptno <> 30;

26、列出在每个部门工作的员工数量, 平均工资和平均服务期限

SELECT
	d.deptno,
	count( e.ename ),
	ifnull( avg( e.sal ), 0 ),
	ifnull( avg( timestampdiff( YEAR, hiredate, now())), 0 ) avgservicetime 
FROM
	emp e
	RIGHT JOIN dept d ON e.deptno = d.deptno 
GROUP BY
	d.deptno;

27、 列出所有员工的姓名、部门名称和工资

SELECT
	e.ename,
	d.dname,
	e.sal 
FROM
	emp e
	JOIN dept d ON e.deptno = d.deptno

28、列出所有部门的详细信息和人数

SELECT
	d.*,
	count( e.deptno ) 
FROM
	dept d
	LEFT JOIN emp e ON d.deptno = e.deptno 
GROUP BY
	d.deptno;

29、列出各种工作的最低工资及从事此工作的雇员姓名

SELECT
	e.ename,
	t.* 
FROM
	emp e
	JOIN ( SELECT job, min( sal ) minsal FROM emp GROUP BY job ) t ON e.job = t.job 
	AND e.sal = t.minsal;

31、列出所有员工的 年工资, 按 年薪从低到高排序

SELECT
	ename,
	(sal + ifnull( comm, 0 )) * 12 yearsal 
FROM
	emp 
ORDER BY
	yearsal ASC

32、求出员工领导的薪水超过3000的员工名称与领导

SELECT
	a.ename,
	b.ename 
FROM
	emp a
	JOIN emp b ON a.mgr = b.empno 
	AND b.sal > 3000

33、求出部门名称中, 带’S’字符的部门员工的工资合计、部门人数

SELECT
	d.deptno,
	d.dname,
	ifnull( sum( e.sal ), 0 ),
	count( e.deptno ) 
FROM
	emp e
	RIGHT JOIN dept d ON e.deptno = d.deptno 
WHERE
	d.dname LIKE '%S%' 
GROUP BY
	d.deptno

34、给任职日期超过 30 年的员工加薪 10%.

select sal*1.1 from emp where timestampdiff(YEAR, hiredate, now()) > 30;

总结:

  1. 要使用多张表的信息时,先进行表连接,最后考虑条件
  2. 分步写sql,一步一步来
  3. 明确语句执行顺序
  4. 常用的规则要使用,比如说运算之前用ifnull判空
  5. 日期函数的计算
  6. 多写多练,熟能生巧。
;