1.关系型数据库设计规则
2.基本的select语句
3.运算符
4.排序与分页
5.多表查询
6.函数的分类
7.SQL底层执行原理
8.子查询
9.创建管理数据库(表)
10.数据处理之增删改
11.MySQL数据类型
12.约束
一、关系型数据库设计规则
- 关系型数据库的典型数据结构就是
数据表
,这些数据表的组成都是结构化的(Structured) - 将数据放到表中,表再放到库中
- 一个数据库中可以有多个表,每个表都有一个名字,用来标识自己,表名具有唯一性
- 表具有一些特性,这些特性定义了数据在表中如何储存,类似Java和Python中“类”的设计
1.1 表、记录、字段
- E-R模型中有3个主要概念:
实体集
属性
联系集
- 一个实体集(class)对应于数据库中的一个表(table),一个实体(instance)则对应于数据库表中的一行(row),也称为一条记录(record),一个属性(attribute)对应数据表中的一列(column),也称为一个字段(field)
1.2 表联系
- 表与表之间的数据记录有关系(relationship)。
- 四种:一对一关联,一对多关联,多对多关联,自我引用
1.2.1 一对一关联(one-to-one)
- 在实际的开发中应用不多,因为一对一可以建成一张表
- 举例:设计
学生表
:学号、姓名、手机号、班级、身份证、家庭住址…- 拆分为两个表:两个表的记录是一一对应的关系
基础信息表
(常用信息):学号、姓名、手机、班级档案信息表
(不常用信息):学号、身份证、家庭住址…
- 两种建表原则:
- 外键唯一:主表的主键和从表的外键(唯一),形成主外键关系,外键唯一
- 外键是主键:主表的主键和从表的主键,形成主外键关系
1.2.2 一对多关系(one-to-many)
- 常见实例场景:
客户表和订单表
,分类表和商品表
,部门表和员工表
- 举例:
- 员工表:编号、姓名、…、所属部门
- 部门表:编号、名称、简介
- 一对多建表原则:在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键
1.2.3 多对多关系(many-to-many)
要表示多对多关系,必须创建第三个表,该表通常称为联系表
,他将多对多关系划分为两个一对多关系,将这两个表的的主键都插入到第三个表中
1.2.4 自我引用(Self referencce)
举例:
员工编号 | 姓名 | 主管编号 |
---|---|---|
101 | 张三 | NULL |
102 | 李四 | 101 |
103 | 王五 | 102 |
二、基本的select语句
2.1 SQL的分类
- DDL(数据定义语言):这些语句定义了不同的数据库、表、视图、索引等数据库对象,还可以用来创建、删除、修改数据库和表的结构
- 主要的语句关键字包括
CREATE
、DROP
、ALTER
、RENAME
、TRUNCATE
等 - DDL 操作一旦执行,则不能回滚。
- 主要的语句关键字包括
- DML(数据操作语言):用于添加、删除、更新和查询数据库记录,并检查数据完整性
- 主要的语句关键字包括
INSERT
、SELECT
、DELETE
、UPDATE
等 - DML 操作一旦执行,默认情况下是不能回滚的,但如果在执行DML 之前,执行了
SET autocommit = FALSE
,则执行DML 操作就可以回滚。
- 主要的语句关键字包括
- DCL(数据控制语言):用于定义数据库、表、字段、用户的访问权限和安全级别
- 主要的关键字包括:
GRANT
、REVOKE
、COMMIT
、ROLLBACK
、SAVEPOINT
等
- 主要的关键字包括:
因为查询语句使用的非常频繁,所以很多人把查询语句单拎出来一类:DQL(数据查询语言)。
还有单独将COMMIT、ROLLBACK 取出来称为TCL(事务控制语言)
2.1.1 导入现有的数据表、表的数据
方式一:source 文件的全路径名
方式二:基于具体的图形化界面的工具导入
2.2 select 基本语句
2.2.1 select…
select 1; # 没有任何子句
select 1+2,2+3; # 没有任何子句
2.2.2 select … from …
SELECT 字段名(列名) FROM 表名
2.2.3 列的别名
注:列的别名只能在ORDER BY
中使用,不能在 WHERE
中使用,因为sql的执行顺序为 FROM
=> WHERE
=> SELECT
=> ORDER BY
# AS可以省略
# 别名可以使用双引号("")引起来
SELECT 列名 AS 别名 FROM 表名
SELECT employees_id emp_id,last_name AS lname,department_id "dept_id",salary * 12 "annual sal"
FROM employees;
2.2.4 去除重复行
DISTINCT
关键字
# 查询部门表中有哪些部门id
SELECT deparment_id FROM employees; # 没有去重的情况
SELECT DISTINCT department_id FROM employees; # 去重后的
# 部门和工资一样得多条记录才会进行去重
SELECT DISTINCT department_id,salary FROM employees;
2.2.5 空值参与运算
- 空值参与运算结果一定也为null
#空值:null
# null 不等同于 0 , '' , 'null'
# 查询月工资和年工资(年工资需要算上奖金率)
SELECT employee_id,salary "月工资",salary * (1 + commission_pct) * 12 "年工资"
FROM employees; # 如果commission_pct为null年工资结果就为null
# 正确写法
SELECT employee_id,salary "月工资",salary * (1 + IFNULL(commission_pct)) * 12 "年工资"
FROM employees;
2.2.6 着重号(` `)
如果表名或者字段名与关键字冲突了,必须使用着重号引起来
SELECT * FROM order; # 报错,因为order是关键字
SELECT * FROM `order`; # 正确写法
2.2.7 查询常数
表中没有这个字段,但是查询的时候需要显示在表中,并且所有记录的常数名都一样
SELECT "常数名",123,employee_id,last_name
FROM employees;
2.3 显示表结构
DESCCRIBE
或DESC
DESCRIBE employees;
DESC employees;
2.4 过滤数据
SELECT … FROM … WHERE 过滤条件
# 查询90号部门的员工信息
SELECT * FROM employees WHERE department_id=90;
三、运算符
3.1 算数运算符
+ 、 - 、* 、 /(DIV) 、 %(MOD)
注:在sql中, + 没有连接的作用,数字与字符串相加,会把字符串转换为数字
SELECT 100 + '1' FROM DUAL; # 结果为101
SELECT 100 + 'a' FROM DUAL; # 结果为100 ,'a'转换为了0
SELECT 100 + null FROM DUAL; # 结果为null,null值参与运算结构都为null;
3.2 比较运算符
3.2.1 = <=> <>(!=) > >= < <=
SELECT 1=NULL,NULL=NULL FROM DUAL; # 只要有null参与判断,结果都为null
<=> 安全等于:在没有null参与判断的情况下,作用与 = 相同,当有null参与时,只有null与null相等
SELECT 1 <=> NULL; # 0
SELECT null <=> null; # 1
3.2.2 是否为null
IS NULL
IS NOT NULL
ISNULL()
# 查询表中commission_pct字段为null的数据有哪些
SELECT last_name,salary,commission_pct
FROM employees
WHERE commission_pct IS NULL;
# 或
SELECT last_name,salary,commission_pct
FROM employees
WHERE ISNULL(commission_pct);
# 或
SELECT last_name,salary,commission_pct
FROM employees
WHERE commission_pct <=> NULL;
# 查询表中commission_pct字段不为null的数据有哪些
SELECT last_name,salary,commission_pct
FROM employees
WHERE commission_pct IS NOT NULL;
# 或
SELECT last_name,salary,commission_pct
FROM employees
WHERE NOT commission_pct <=> NULL;
3.2.3 最小、最大
LEAST
GREATEST
SELECT LEAST('A','D','E','F'),GREATEST('A','D','E','F')
FROM DUAL; # 输出 A , F
3.2.4 区间(大于等于多少并且小于等于多少)
BETWEEN ... (下限) AND (上限) ...
#查询工资在6000到8000之间的员工
SELECT last_name,salary
FROM employees
WHERE salary BETWEEN 6000 AND 8000;
#查询工资不在6000到8000之间的员工
SELECT last_name,salary
FROM employees
WHERE salary NOT BETWEEN 6000 AND 8000;
# 或
SELECT last_name,salary
FROM employees
WHERE salary < 6000 OR salary > 8000;
3.2.5 包含
IN (集合)
NOT IN (集合)
# 查询部门为10,20,30的员工的信息
SELECT last_name,salary
FROM employees
WHERE department_id IN (10,20,30);
# 查询工资不为6000,7000,8000的员工
SELECT last_name,salary
FROM employees
WHERE salary NOT IN (6000,7000,8000);
3.2.6 模糊查询
LIKE
%
表示不确定个数的字符(0个或多个)
_
一个_代表一个不确定的·字符
同一组%中可以写多个,如:
%a%e%
但是是有顺序限制的
# 查询last_name字段中包含'a'的员工的信息
SELECT last_name,salary
FROM employees
WHERE last_name LIKE '%a%';
# 查询last_name字段中包含'a' 并且 包含字符 'e' 的员工的信息
SELECT last_name,salary
FROM employees
WHERE last_name LIKE '%a%' AND last_name LIKE '%e%';
# 或
SELECT last_name,salary
FROM employees
WHERE last_name LIKE '%a%e%' OR last_name LIKE '%e%a%';
# 查询last_name中第二个字符为'a'的员工信息
SELECT last_name,salary
FROM employees
WHERE last_name LIKE '_a%';
# 查询last_name中第一个字符为'_'的 并且 第三个字符为'a'的员工信息
SELECT last_name
FROM employees
WHERE last_name LIKE '_\_a%';
3.2.7 REGEXP \ RLIKE : 正则表达式
REGEXP
运算符用来匹配字符串,语法格式为expr REGEXP 匹配条件
。如果expr满足匹配条件,返回1,如果不满足,则返回0。若expr或匹配条件任意一个为null,则结果为null。
REGEXP运算符在进行匹配时,常用的有下面几种通配符:
1.
^
匹配以该字符后面的字符开头的字符串
2.$
匹配以该字符前面的字符结尾的字符串
3..
匹配任意一个单字符
4.[......]
匹配在方括号内的任意字符。例如[a,b,c]
匹配“a”或者“b”或者“c”。为了命名字符的范围,使用一个“-”,[a-z]
匹配任何字母,而[0-9]
匹配任何数字。
5.*
匹配0个或者多个在他前面的字符。如:x*
匹配任何数量的’x’字符,[0-9]*
匹配任何数量的数字,而*
匹配任何数量的任何字符。
SELECT 'shark' REGEXP '^sha','shark' REGEXP 'k$', 'shark' REGEXP '[a-z]'
FROM DUAL; # 输出1,1,1
3.3 逻辑运算符
逻辑非:NOT
!
逻辑与:AND
&&
两边条件都为true 则返回true
逻辑或:OR
||
两边条件有一个为true 则 返回 true
逻辑异或:XOR
两边的条件 一 true 一 false则返回true
AND
的优先级是高于OR
四、排序与分页
4.1 排序数据
4.1.1 排序规则
- 使用
ORDER BY
子句进行排序。不指明排序方法,默认情况下是按照ASC
升序进行排序ASC
(accend) 升序DESC
(descend) 降序
- 可以适应列的别名进行排序
4.1.2 单列排序
# 员工工资从高到低进行排序
SELECT last_name,salary
FROM employees
ORDER BY salary DESC;
4.2.2 多列排序
- 在对多列进行排序的时候,首先排序的第一列必须有相同的值,才会对第二列进行排序。如果第一列中所有的值都是唯一的,将不在对第二列进行排序。
# 显示员工信息表,按照department_id降序排序,salary的升序排序
SELECT last_name,salary,department_id
FROM employees
ORDER BY department_id DESC,salary;
4.2 分页
分页公式: 偏移量 = ( 当前页 - 1 ) * 一页的数据量
4.2.1 实现
LIMIT
LIMIT [数据偏移量,]行数
# 查找第一页数据,每页20条数据
SELECT employee_id,last_name
FROM employees
LIMIT 0,20
# 查找第二页数据,每页20条数据
SELECT employee_id,last_name
FROM employees
LIMIT 20,20
# 查找第三页数据,每页20条数据
SELECT employee_id,last_name
FROM employees
LIMIT 20 OFFSET 40; # OFFSET 是mysql8的新特性,代表偏移量 与LIMIT 40,20 相同
五、多表查询
- 多表查询需要有连接条件,若没有连接条件,会造成笛卡尔积错误。
- 如果查询语句中出现了多个表中都存在的字段,必须指明次字段所在的表
表名.字段名
- 建议:从sql优化的角度讲,在多表查询时,建议每个字段前都指明其所在的表
- 如果有n个表实现多表查询,则需要至少 n - 1个连接条件
employees; # 员工表
departments; # 员工部门表
locations; # 员工位置表
员工部门表(主表)=> 员工表(从表)department_id
员工位置表(主表)=> 员工部门表(从表)location_id
5.1 等值连接 vs 非等值连接
5.1.1 等值连接
# 查询所有员工的部门名称
SELECT emp.department_id,dept.部门名称字段 # 不想翻了
FROM employees AS emp,departments AS dept
WHERE emp.department_id = dept.department_id;
5.1.2 非等值连接
# 查询每个员工的工资是属于哪个等级
SELECT emp.last_name,emp.salary,jgrad.grade_level
FROM employees AS emp,job_grades as jgrad
WHERE emp.salary BETWEEN jgrad.lowest_sal AND jgrad.highest_sal;
5.2 自连接 vs 非自连接
5.2.1 自连接
- 物理上是同一张表
- 逻辑上是两张表
# 查询员工id。姓名极其管理者的id和姓名
SELECT emp.employee_id,emp.last_name,mgr.employee_id,mgr.last_name
FROM employees AS emp,employees AS mgr
WHERE emp.employee_id = mgr.manager_id;
5.2.2 非自连接
- 只要不是自我引用的多表查询方式,都属于非自连接
5.3 内连接 vs 外连接
- 内连接:合并具有同一列的两个以上的表的行,结果集中不包含一个表与另一个表不匹配的行
- 外连接:两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行,这种连接称为左(或右)连接。没有匹配的行时,结果表中相应的列为null
- 如果是左(右)外连接,则连接条件中左(右)边的表称为
主表
,右(左)边的表称为从表
5.3.1 内连接
[INNER] JOIN...ON..
# 内连接
#两个表内连接
SELECT emp.last_name,dept.department_name
FROM employees AS emp JOIN departments AS dept
ON emp.department_id = dept.department_id;
# 三个或多个表内连接
SELECT emp.last_name,dept.department_name,l.city
FROM employees AS emp JOIN departments AS dept
ON emp.department_id = dept.department_id
JOIN locations AS l
ON dept.location_id = l.location_id;
5.3.2 左外连接
LEFT [OUTER] JOIN... ON...
# 查询所有员工的 lasr_name,department_name
SELECT emp.last_name,dept.department_name
FROM employees AS emp LEFT JOIN departments AS dept
ON emp.department_id = dept.department_id;
5.3.2 右外连接
RIGHT [OUTER] JOIN... ON...
SELECT emp.last_name,dept.department_name
FROM employees AS emp RIGHT JOIN departments AS dept
ON emp.department_id = dept.department_id;
5.3.3 满(全)外连接(MySQL不支持)
SELECT emp.last_name,dept.department_name
FROM employees AS emp FULL JOIN departments AS dept
ON emp.department_id = dept.department_id;
5.3.4 UNION的使用
作用: 合并查询结果
利用UNION关键字,可以给出多条SELECT语句,并将他们的结果组成单个结果集合并时,两个表对应的列数和数据类型必须相同,并且相互对应。各个SELECT语句之间使用UNION
或UNION ALL
关键字分割
语法格式:
SELECT column,... FROM table1
UNION [ALL]
SELECT column,... FROM table2
UNION
操作符:UNION
操作符返回两个查询的结果集的并集,去除重复记录UNION ALL
擦作符:UNION ALL
操作符返回两个查询的结果集的并集。对于结果集重复的部分,不去重。
注意:执行
UNION ALL
语句是所需要的资源比UNION
少,如果明确知道合并后的数据不存在重复数据,或者不需要去除重复的数据,则尽量使用UNION ALL
语句,以提高数据查询效率
5.4 自然连接 vs 非自然连接(SQL99新特性)
- 自然连接
NATURAL JOIN
自然连接相当于SQL92中的等值连接,它会帮助你自动查询两张接连表中所有相同的字段
,然后进行等值连接
SELECT emp.employee_id,emp.last_name,dept.department_name
FROM employees AS emp JOIN departments AS dept
ON emp.department_id = dept.department_id
AND emp.manager_id = dept.manager_id;
# 相当于
SELECT emp.employee_id,emp.last_name,dept.department_name
FROM employees AS emp NATURAL JOIN departments AS dept;
- USING
USING
指定数据表的同名字段
进行等值连接。但是只能配合JOIN
一起使用
SELECT emp.employee_id,emp.last_name,dept.department_name
FROM employees AS emp JOIN departments AS dept
ON emp.department_id = dept.department_id;
# 相当于
SELECT emp.employee_id,emp.last_name,dept.department_name
FROM employees AS emp JOIN departments AS dept
USING (department_id);
5.5 7种JOIN的实现
A => 员工表 employees
B => 部门表 departments
- 中图:内连接
SELECT emp.last_name,dept.department_name
FROM employees AS emp JOIN departments AS dept
ON emp.department_id = dept.department_id;
- 左上图:左外连接
SELECT emp.last_name,dept.department_name
FROM employees AS emp LEFT JOIN departments AS dept
ON emp.department_id = dept.department_id;
- 右上图:右外连接
SELECT emp.last_name,dept.department_name
FROM employees AS emp RIGHT JOIN departments AS dept
ON emp.department_id = dept.department_id;
- 左中图:
SELECT emp.last_name,dept.department_name
FROM employees AS emp LEFT JOIN departments AS dept
ON emp.department_id = dept.department_id
WHERE dept.department_id IS NULL;
- 右中图:
SELECT emp.last_name,dept.department_name
FROM employees AS emp RIGHT JOIN departments AS dept
ON emp.department_id = dept.department_id
WHERE emp.department_id IS NULL;
- 左下图:满外连接
# 方式一:左上图 UNION ALL 右中图
SELECT emp.last_name,dept.department_name
FROM employees AS emp LEFT JOIN departments AS dept
ON emp.department_id = dept.department_id
UNION ALL
SELECT emp.last_name,dept.department_name
FROM employees AS emp RIGHT JOIN departments AS dept
ON emp.department_id = dept.department_id
WHERE emp.department_id IS NULL;
# 方式二:左中图 UNION ALL 右上图
SELECT emp.last_name,dept.department_name
FROM employees AS emp LEFT JOIN departments AS dept
ON emp.department_id = dept.department_id
WHERE dept.department_id IS NULL
UNION ALL
SELECT emp.last_name,dept.department_name
FROM employees AS emp RIGHT JOIN departments AS dept
ON emp.department_id = dept.department_id;
- 右下图:
# 左中图 UNION ALL 右中图
SELECT emp.last_name,dept.department_name
FROM employees AS emp LEFT JOIN departments AS dept
ON emp.department_id = dept.department_id
WHERE dept.department_id IS NULL
UNION ALL
SELECT emp.last_name,dept.department_name
FROM employees AS emp RIGHT JOIN departments AS dept
ON emp.department_id = dept.department_id
WHERE emp.department_id IS NULL;
注意:
我们要
控制连表的数量
,多表连接就相当于嵌套for循环一样,非常消耗资源,会让sql的查询性能下降的很严重,因此不要连接不必要的表。在许多DBMS中,也都会有最大连表的限制
【强制】超过三个表禁止join。需要join的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引
说明:即使双表查询也要注意表索引、SQL性能。
六、函数的分类
6.1 单行函数(太多了,百度吧)
获取时间戳:UNIX_TIMESTAMP()
6.2 流程控制函数
函数 | 用法 |
---|---|
IF(value,value1,value2) | 如果value的值为true,则返回value1,否则返回value2 |
IFNULL(value1,value2) | 如果value1不为null返回value1,否则返回value2 |
CASE WHEN 条件1 THEN 结果1 WHEN 条件2 THEN 结果2 … [ELSE 结果3] END | 相当于 if…else if … else… |
CASE expr WHEN 常量值1 THEN 值1 WHEN 常量值2 THEN 值2 … [ELSE 值n] END | 相当于 switch … case… |
6.3 聚合函数(常见的5个聚合函数)
6.3.1 AVG (平均数) / SUM(总和)
SELECT AVG(salary),SUM(salary)
FROM employees;
6.3.2 MAX(最大) / MIN(最小)
适用于数值类型,字符串类型,日期时间类型的字段
SELECT MAX(salary),MIN(salary)
FROM employees;
6.3.3 COUNT(数量)
作用: 计算指定字段在查询结果中出现的个数
注意:计算指定字段出现的个数时,是不计算空值的
- 公式:AVG = SUM / COUNT
6.4 GROUP BY(分组查询)
SELECT中出现的非聚合函数的字段必须声明在GROUP BY中。反之,GROUP BY中声明的字段可以不出现在SELECT中
# 查询各个部门的平均工资
SELECT department_id,AVG(salary),SUM(salary)
FROM employees
GROUP BY department_id;
6.5 HAVING的使用(过滤数据)
- 如果过滤条件中使用了聚合函数,则必须使用
HAVING
代替WHERE
。否则报错。 - 如果有
GROUP BY
,HAVING
必须声明在ORDER BY
的后边。
# 查询每个部门的最高工资并且大于10000
SELECT department_id,MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary) > 10000
ORDER BY AVG(salary) ASC;
# 查询部门编号为10,20,30,40的部门的最高工资并且大于10000
# 推荐
SELECT department_id,MAX(salary)
FROM employees
WHERE department_id IN (10,20,30,40)
GROUP BY department_id
HAVING MAX(salary) > 10000;
# 或
SELECT department_id,MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary) > 10000 AND department_id IN (10,20,30,40);
七、SQL底层执行原理
7.1 SELECT语句的完整结构
7.1.1 SQL92 语法:
SELECT ...,...,... (存在聚合函数)
FROM ...,...,...
WHERE 多表的连接条件 AND 不包含聚合函数的过滤条件
GROUP BY ...,...
HAVING 包含聚合函数的过滤条件
ORDER BY ...,...(ASC / DESC)
LIMIT ...
7.1.2 SQL99 语法:
SELECT ...,...,... (存在聚合函数)
FROM ... (LEFT / RIGHT) JOIN ... ON 多表的连接条件
(LEFT / RIGHT) JOIN ... ON 多表的连接条件
WHERE 不包含聚合函数的过滤条件
GROUP BY ...,...
HAVING 包含聚合函数的过滤条件
ORDER BY ...,...(ASC / DESC)
LIMIT ...
7.2 SQL语句的执行过程
其实在每一个步骤都会产生一个虚拟表
FROM
=> ON
=> (LEFT / RIGHT) JOIN
=> WHERE
=> GROUP BY
=> HAVING
=> SELECT
=> DISTINCT
=> GROUP BY
=> LIMIT
八、子查询
# 查询谁的工资比Abel高
SELECT last_name,salary
FROM employees
WHERE salary > (SELECT salary
FROM employees
WHERE last_name = 'Abel');
8.1.1 称谓的规范:外查询(或主查询),内查询(或子查询)
- 子查询在主查询之前一次执行完成
- 子查询的结果被主查询使用
- 注意事项:
- 子查询要包含在括号内
- 将子查询放在比较条件的右侧
- 单行操作符对应单行子查询,多行操作符对应多行子查询
8.2 子查询分类
角度1:按子查询返回的结果行数
分为:
- 单行子查询
- 多行子查询
角度2:按内查询是否被执行多次
分为:
- 相关子查询
- 不相关子查询
8.3 单行子查询
单行子查询比较操作符:=
>
>=
<
<=
!=
<>
# 查询与 141 号员工的 manager_id,department_id相同的其他员工的employee_id,manager_id,department_id
SELECT employee_id,manager_id,department_id
FROM employees
WHERE manager_id = (SELECT manager_id
FROM employees
WHERE employee_id = 141)
AND department_id = (SELECT department_id
FROM employees
WHERE employee_id = 141)
AND employee_id != 141;
8.3.1 HAVING中的子查询
# 查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT department_id,MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary) > (SELECT MIN(salary)
FROM employees
WHERE department_id = 50);
8.3.1 CASE中的子查询
# 显示员工的employee_id,last_name和location,其中,若员工department_id与location_id为1800的department_id相同,则location为‘Canada’,其余则为‘USA’
SELECT employee_id,last_name,(CASE department_id
WHEN (SELECT department_id FROM departments WHERE location_id = 1800) THEN
"Canada"
ELSE
"USA"
END) "location"
FROM employees;
8.4 多行子查询
多行子查询比较操作符:
操作符 | 含义 |
---|---|
IN | 等于列表中的任意一个 |
ANY | 需要和单行比较操作符符一起使用,和子查询返回的某一个值比较 |
ALL | 需要和单行比较操作符符一起使用,和子查询返回的所有值比较 |
SOME | 实际上是ANY的别名,作用相同,一般常用ANY |
# 查询平均工资最低的部门
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) = (SELECT MIN(avg_salary)
FROM (SELECT AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id) AS t_dept_avg);
# 或
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) <= ALL (SELECT AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id);
8.5 相关子查询
8.5.1相关子查询执行流程
如果子查询的执行依赖于外部查询,通常情况下都是因为子查询的表用到了外部的表,并进行了条件关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询称为关联子查询
。
相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询
说明:子查询中使用主查询中的列
8.5.2代码示例
在WHERE
中使用相关子查询
# 查询员工中工资大于本部门平均工资的员工的last_name,salary,和其department_id
SELECT last_name,salary,department_id
FROM employees emp1
WHERE salary > (
SELECT AVG(salary)
FROM employees emp2
WHERE department_id = emp1.department_id
);
综合习题:
#1.查询和Zlotkey相同部门的员工姓名和工资
SELECT last_name,salary
FROM employees
WHERE department_id = (
SELECT department_id
FROM employees
WHERE last_name="Zlotkey"
);
#2.查询工资比公司平均工资高的员工的员工号,姓名和工资
SELECT employee_id,last_name,salary
FROM employees
WHERE salary > (
SELECT AVG(salary)
FROM employees
);
#3.选择工资大于所有JOB_ID = 'SA_MAN'的员工的工资的员工的last_name, job_id, salary
SELECT last_name,job_id,salary
FROM employees
WHERE salary > ALL (
SELECT salary
FROM employees
WHERE job_id = 'SA_MAN'
);
#4.查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名
SELECT employee_id,last_name
FROM employees
WHERE department_id = ANY (
SELECT DISTINCT department_id
FROM employees
WHERE last_name LIKE '%u%'
);
#5.查询在部门的location_id为1700的部门工作的员工的员工号
SELECT employee_id
FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE location_id = 1700
);
#6.查询管理者是King的员工姓名和工资
SELECT last_name,salary
FROM employees
WHERE manager_id IN (
SELECT employee_id
FROM employees
WHERE last_name = 'King'
);
#7.查询工资最低的员工信息: last_name, salary
SELECT last_name,salary FROM employees WHERE salary = ( SELECT MIN(salary) FROM employees );
#8.查询平均工资最低的部门信息
SELECT *
FROM departments
WHERE department_id = (
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) <= ALL (
SELECT AVG(salary)
FROM employees
GROUP BY department_id
)
);
#9.查询平均工资最低的部门信息和该部门的平均工资(相关子查询)
SELECT d.*,(SELECT AVG(salary) FROM employees WHERE department_id = d.department_id) avg_sal
FROM departments d
WHERE department_id = (
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) <= ALL (
SELECT AVG(salary)
FROM employees
GROUP BY department_id
)
);
#10.查询平均工资最高的 job 信息
SELECT *
FROM jobs
WHERE job_id = (
SELECT job_id
FROM employees
GROUP BY job_id
HAVING AVG(salary) = (
SELECT MAX(max_salary)
FROM (
SELECT AVG(salary) max_salary
FROM employees
GROUP BY job_id
) t_salary
)
);
#11.查询平均工资高于公司平均工资的部门有哪些?
SELECT department_id,department_name
FROM departments
WHERE department_id IN (
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) > (
SELECT AVG(salary)
FROM employees
)
)
#12.查询出公司中所有 manager 的详细信息
SELECT *
FROM employees
WHERE employee_id IN (
SELECT manager_id
FROM employees
);
#13.各个部门中 最高工资中最低的那个部门的 最低工资是多少?
SELECT MIN(salary)
FROM employees
WHERE department_id = (
SELECT department_id
FROM employees
GROUP BY department_id
HAVING MAX(salary) = (
SELECT MIN(max_salary)
FROM (
SELECT MAX(salary) max_salary
FROM employees
GROUP BY department_id
) t_salary_max
)
)
#14.查询平均工资最高的部门的 manager 的详细信息: last_name, department_id, email, salary
SELECT last_name, department_id, email, salary
FROM employees
WHERE employee_id = (
SELECT manager_id
FROM departments
WHERE department_id = (
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) = (
SELECT MAX(max_salary)
FROM (
SELECT AVG(salary) max_salary
FROM employees
GROUP BY department_id
) t_max_salary
)
)
);
#15. 查询部门的部门号,其中不包括job_id是"ST_CLERK"的部门号
SELECT department_id
FROM departments d
WHERE department_id NOT IN (
SELECT DISTINCT department_id
FROM employees
WHERE job_id = 'ST_CLERK'
);
# 选择所有没有管理者的员工的last_name
SELECT last_name
FROM employees
WHERE manager_id IS NULL;
#17.查询员工号、姓名、雇用时间、工资,其中员工的管理者为 'De Haan'
SELECT employee_id,last_name,hire_date,salary
FROM employees
WHERE manager_id = (
SELECT employee_id
FROM employees
WHERE last_name = 'De Haan'
);
#18.查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资(相关子查询)
SELECT employee_id,last_name,salary
FROM employees emp1
WHERE salary > (
SELECT AVG(salary)
FROM employees emp2
WHERE emp1.department_id = emp2.department_id
);
#19.查询每个部门下的部门人数大于 5 的部门名称(相关子查询)
SELECT department_name
FROM departments d
WHERE 5 < (
SELECT COUNT(*)
FROM employees e
WHERE d.department_id = e.department_id
);
#20.查询每个国家下的部门个数大于 2 的国家编号(相关子查询)
SELECT country_id
FROM locations l
WHERE 2 < (
SELECT COUNT(*)
FROM departments d
WHERE l.`location_id` = d.`location_id`
);
九、创建管理数据库(表)
9.1 创建数据库
- 方式1:创建数据库
CREATE DATABASE 数据库名
- 方式2:创建数据库并指定字符集
CREATE DATABASE 数据库名 CHARACTER SET 字符集;
- 方式3:判断是否存在数据库,不存在则创建(推荐)
CREATE DATABASE IF NOT EXISTS 数据库名
# 查看创建数据库详情(默认设置的数据库类型,字符集...)
SHOW CREAETE DATABASE 数据库名
9.2 管理数据库
- 查看当前连接中的数据库有哪些
SHOW DATABASES;
- 切换数据库
use 数据库名
- 查看当前数据库中保存的数据表
SHOW TABLES;
- 查看当前正在使用的数据库
SELECT DATABASE() [FROM DUAL];
- 查看指定数据库下保存的数据表
SHOW TABLES FROM 数据库名;
9.3 修改数据库
注意:数据库的表明是无法修改的
- 更改数据库字符集
ALTER DATABASE 数据库名 CHARACTER SET '字符集'
9.4 删除数据库
- 方式1:
DROP DATABASE 数据库名;
- 方式2:(推荐)
DROP DATABASE IF EXISTS 数据库名;
9.5 MySQL 的数据类型
类型 | 类型举例 |
---|---|
整数类型 | TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT |
浮点类型 | FLOAT、DOUBLE |
定点数类型 | DECIMAL |
位类型 | BIT |
日期时间类型 | YEAR、TIME、DATE、DATETIME、TIMESTAMP |
文本字符串类型 | CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT |
枚举类型 | ENUM |
集合类型 | SET |
二进制字符串类型 | BINARY、VARBINARY、TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB |
JSON类型 | JSON对象、JSON数组 |
空间数据类型 | 单值:GEOMETRY、POINT、LINESTRING、POLYGON;集合:MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、 GEOMETRYCOLLECTION |
其中,常用的几类介绍如下:
数据类型 | 描述 |
---|---|
INT | 从 -231 到 231-1 的整型数据。存储大小为 4个字节 |
CHAR(size) | 定长字符数据。若未指定,默认为1个字符,最大长度255 |
VARCHAR(size) | 可变长字符数据,根据字符串实际长度保存,必须指定长度 |
FLOAT(M,D) | 单精度,占用4个字节,M=整数位+小数位,D=小数位。 D<=M<=255,0<=D<=30,默认M+D<=6 |
DOUBLE(M,D) | 双精度,占用8个字节,D<=M<=255,0<=D<=30,默认M+D<=15 |
DECIMAL(M,D) | 高精度小数,占用M+2个字节,D<=M<=65,0<=D<=30,最大取值范围与DOUBLE相同。 |
DATE | 日期型数据,格式’YYYY-MM-DD’ |
BLOB | 二进制形式的长文本数据,最大可达4G |
TEXT | 长文本数据,最大可达4G |
9.6 数据表操作
9.6.1 创建数据表(需要用户具备创建表的权限)
方式一:
CREATE TABLE IF NOT EXISTS 表名 (
字段名1 数据类型 [约束条件] [默认值],
字段名2 数据类型 [约束条件] [默认值],
字段名3 数据类型 [约束条件] [默认值],
......
[表约束条件]
);
方式二:基于现有的表
CREATE TABLE 表名
AS
SELECT employee_id,last_name,salary
FROM employees;
9.6.2修改表 --> ALTER TABLE
- 添加一个字段
ALTER TABLE
表名
ADD
字段名 数据类型 [约束条件] [默认值]
[FIRST | AFTER]
字段名
# 默认添加到最后
ALTER TABLE myemp1
ADD emp_address2 VARCHAR(40) NOT NULL
# 添加到某个字段之后
ALTER TABLE myemp1
ADD emp_address2 VARCHAR(40) NOT NULL
AFTER id;
# 添加到第一个
ALTER TABLE myemp1
ADD emp_address3 VARCHAR(40) NOT NULL
FIRST;
- 修改一个字段:数据类型,长度,默认值
ALTER TABLE
表名
MODIFY
字段名 数据类型 [约束条件] [默认值]
ALTER TABLE myemp1
MODIFY emp_address2 VARCHAR(40);
- 重命名一个字段
ALTER TABLE
表名
CHANGE
旧字段名 新字段名 数据类型 [约束条件] [默认值]
ALTER TABLE myemp1
CHANGE emp_address2 emp_address4 VARCHAR(49);
- 删除一个字段
ALTER TABLE
表名
DROP COLUMN
字段名
ALTER TABLE myemp1
DROP COLUMN emp_address4;
9.6.3重命名表
- 方式一:
RENAME TABLE
表名
TO
新表名
RENAME TABLE myemp1
TO myemp11;
- 方式二:
ALTER TABLE
表名
RENAME TO
新表名
ALTER TABLE myemp11
RENAME TO myemp1;
9.6.4删除表
DROP TABLE
表名
9.6.5清空表
TRUNCATE TABLE
表名
TRUNCATE
和 DELETE
对比:
- 相同点:都可以实现删除表中的数据,同时保留表结构
- 不同点:
TRUNCATE
执行完后数据不能回滚DELETE
执行完后数据可以回滚
9.7 DCL中的 COMMIT 和 ROLLBACK
COMMIT
:提交数据,一旦执行COMMIT,则数据就被永久的保存在了数据库中,意味着数据不可以回滚。ROLLBACK
: 回滚数据,一旦执行ROLLBACK,则可以实现数据的回滚,回滚到最近一次的COMMIT之后。
对比 TRUNCATE
和 DELETE
:
DELETE
COMMIT;
SELECT * from emp3;
SET autocommit = FALSE;
DELETE FROM emp3;
SELECT * from emp3;
ROLLBACK;
SELECT * FROM emp3; # 数据回来了
TRUNCATE
COMMIT;
SELECT * from emp3;
SET autocommit = FALSE;
TRUNCATE TABLE emp3;
SELECT * from emp3;
ROLLBACK;
SELECT * FROM emp3; # 数据为空
十、数据处理之增删改
10.1 插入数据
- 方式一:VALUES的方式添加
① 没有指明添加的字段,必须按照声明时候的字段顺序去依次添加,为空的字段用null填充
INSERT INTO emp1
VALUES (1,"zhangsan","2000-3-16",3400),
(2,"lisi","2000-3-17",3500);
② 指明要添加的字段,可以不添加表中已经声明的字段,默认null填充
INSERT INTO emp1(id,salary,user_name,hire_date)
VALUES (3,5000,'wangwu','2000-4-16');
- 方式二:将查询结果插入到表中
注意:emp1 表中要添加的字段长度不能低于employess表
INSERT INTO emp1 (id,salary,user_name,hire_date)
SELECT employee_id,salary,last_name,hire_date
FROM employees
WHERE department_id IN (60,70);
10.2 更新数据
UPDATE 表名 SET ... WHERE ...
UPDATE emp1
SET hire_date = CURDATE()
WHERE user_name = 'wangwu';
10.3 删除数据
DELETE FROM 表名 WHERE ...
DELETE FROM emp1
WHERE id = 3;
10.4 计算列
在MySQL 8.0 中,CREATE TABLE 和 ALTER TABLE 都支持添加计算列。
例:声明字段a、b、c,其中字段c为计算列,用来计算 a+b的和
CREATE TABLE test1 (
a INT,
b INT,
c INT GENERATED ALWAYS AS (a+b) VIRTUAL
);
INSERT INTO test1 (a,b)
VALUES (10,20);
SELECT * FROM test1;
UPDATE test1
SET a = 100;
十一、MySQL 数据类型
类型 | 类型举例 |
---|---|
整数类型 | TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT |
浮点类型 | FLOAT、DOUBLE |
定点数类型 | DECIMAL |
位类型 | BIT |
日期时间类型 | YEAR、TIME、DATE、DATETIME、TIMESTAMP |
文本字符串类型 | CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT |
枚举类型 | ENUM |
集合类型 | SET |
二进制字符串类型 | BINARY、VARBINARY、TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB |
JSON类型 | JSON对象、JSON数组 |
空间数据类型 | 单值:GEOMETRY、POINT、LINESTRING、POLYGON;集合:MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、 GEOMETRYCOLLECTION |
常见数据类型属性,如下:
MySQL关键字 | 含义 |
---|---|
NULL | 数据列可包含NULL值 |
NOT NULL | 数据列不允许包含NULL值 |
DEFAULT | 默认值 |
PRIMARY KEY | 主键 |
AUTO_INCREMENT | 自动递增,适用于整数类型 |
UNSIGNED | 无符号 |
CHARACTER SET name | 指定一个字符集 |
11.1 整数类型
整数类型一共有5种,包括TINYINT
SMALLINT
MEDIUMINT
INT(INTEGER)
BIGINT
区别如下:
整数类型 | 字节 | 有符号数取值范围 | 无符号数取值范围 |
---|---|---|---|
TINYINT | 1 | -128 ~ 127 | 0 ~ 255 |
SMALLINT | 2 | -32768 ~ 32767 | 0 ~ 65535 |
MEDIUMINT | 3 | -8388608 ~ 8388607 | 0~16777215 |
INT、INTEGER | 4 | -2147483648 ~ 2147483647 | 0 ~4294967295 |
BIGINT | 8 | -9223372036854775808 ~ 9223372036854775807 | 0 ~ 18446744073709551615 |
11.2 定点数类型(DECIMAL)
数据类型 | 字节 | 含义 |
---|---|---|
DECIMAL(M,D) | M + 2字节 | 有效范围由M和D决定 |
- DECIMAL(M,D) 的最大取值范围和DOUBLE一样
- 定点数在MySQL内部是以
字符串
的形式进行存储
11.3 日期与时间类型
YEAR
类型通常用来表示年DATE
类型通常用来表示年、月、日TIME
类型通常用来表示时、分、秒DATETIME
类型通常用来表示年、月、日、时、分、秒TIMESTAMP
类型通常用来表示带时区的年、月、日、时、分、秒
类型 | 名称 | 字节 | 日期格式 | 最小值 | 最大值 |
---|---|---|---|---|---|
YEAR | 年 | 1 | YYYY或YY | 1901 | 2155 |
TIME | 时间 | 3 | HH:MM:SS | -838:59:59 | 838:59:59 |
DATE | 日期 | 3 | YY-MM-DD | 1000-01-01 | 9999-12-03 |
DATETIME | 日期时间 | 8 | YYYY-MM-DD HH:MM:SS | 1000-01-01 00:00:00 | 9999-12-03 23:59:59 |
TIMESTAMP | 日期时间 | 4 | YYYY-MM-DD HH:MM:SS | 1970-01-01 00:00:00 UTC | 2038-01-19 03:14:07 UTC |
11.3.1 YEAR类型
在MySQL中,YEAR有一下几种存储格式:
- 以4位字符串或数字格式表示YEAR类型,其格式为YYYY,最小值为1901,最大值为2155
- 以2位字符串格式表示YEAR类型,最小值为00,最大值为99。
- 当取值为 01 到 69 时,表示 2001 到 2069
- 当取值为 70 到 99 时,表示 1970 到 1999
- 当取值整数的 00 或 0 添加的话,是 0000 年
- 当取值是日期/字符串的’0’添加的话,是2000年
11.3.2 DATE 类型
- 以
YYYY-MM-DD
格式或者YYYYMMDD
格式表示的字符日期,其最小取值为1000-01-01,最大取值为9999-12-03。YYYYMMDD会被转换为YYYY-MM-DD。 - 以
YY-MM-DD
格式或者YYMMDD
格式表示的字符串日期中的两位年份转换规则与YEAR类型相同。 - 使用
CURRENT_DATE()
或者NOW()
函数,会插入当前系统的日期
11.3.3 TIME 类型
在MySQL中,向TIME类型的字段插入数据时,也可以使用几种不同的格式。
- 可以使用带有冒号的字符串,比如
'D HH:MM:SS'
'HH:MM:SS'
'HH:MM'
'D HH:MM'
'D HH'
或'SS'
格式。其中 D 表示天,其最小值为0,最大值为34。如果使用带有格式的字符串插入TIME类型的字段时,D会被转换为小时,计算格式为D*24+HH。当使用带有冒号且不带D的字符串表示时间时,表示当天的事件,比如12:10表示12:10:00,而不是00:12:10 - 可以使用不带有冒号的字符串或者数字,格式为
'HHMMSS'
或者HHMMSS
,如果插入一个不合法的字符串或者数字,在存储数据时,会自动转换为 00:00:00。比如12:10,会将最右边的两位解析成秒,表示00:12:10,而不是12:10:00
11.3.4 DATETIME 类型
- 存储格式为
'YYYY-MM-DD HH:MM:SS'
'YY-MM-DD HH:MM:SS'
'YYYYMMDDHHMMSS'
'YYMMDDHHMMSS'
,也可以是数值格式YYYYMMDDHHMMSS
YYMMDDHHMMSS
11.3.5 TIMESTAMP 类型
- 存储数据的时候需要对当前时间所在的时区进行转换,查询数据的啥时候再讲时间转换为当前的时区。因此,使用TIMESTAMP存储的同一个时间值,在不同的时区查询时会显示不同的时间
修改当前时区:
中国属于东八区
SET time_zone = '+8:00'
11.4 文本字符串类型
文本类型字符串整体上分为CHAR
VARCHAR
TINYTEXT
TEXT
MEDIUMTEXT
LONGTEXT
ENUM
SET
等类型
文本字符串类型 | 值的长度 | 长度范围 | 占用的存储空间 |
---|---|---|---|
CHAR(M) | M | 0<=M<=255 | M个字节 |
VARCHAR(M) | M | 0<=M<=65535 | M+1个字节 |
TINYTEXT | L | 0<=L<=255 | L+2个字节 |
TEXT | L | 0<=L<=65535 | L+2个字节 |
MEDIUMTEXT | L | 0<=L<=16777215 | L+3个字节 |
LONGTEXT | L | 0<=L<=4294967295 | L+4个字节 |
ENUM | L | 1<=L<=65535 | 1或2个字节 |
SET | L | 0<=L<=64 | 1,2,3,4或8个字节 |
11.4.1 ENUM 类型
声明的时候:
CREATE TABLE test(
s ENUM('春','夏','秋','冬','unknow')
);
添加的时候:
INSERT INTO test(s) VALUES ('春');
#或
# 下标从1开始
INSERT INTO test(s) VALUES (1);
每条数据添加的时候只能在声明枚举类型字段中选一个,不能写多个。
11.4.2 SET 类型
与ENUM类型相似,不通点在于SET 可以添加多个
11.5 二进制字符串类型
11.5.1 BINARY和VARBINARY
类似于 CHAR 和 VARCHAR ,不同点在存储的是二进制字符串
十二、约束
12.1 约束的分类
- 角度1:约束的字段的个数:
单列约束 vs 多列约束 - 角度2:约束的作用范围
- 列级约束:将此约束声明在对应字段后边
- 表级约束:将表中所有字段都声明完,在所有字段的后边声明的约束
- 角度3:约束的作用(或功能)
NOT NULL
非空约束UNIQUE
唯一性约束PRIMARY KEY
主键约束FOREIGN KEY
外键约束CHECK
检查约束DEFAULT
默认值约束